Help writing a differential custom loss function with logic

username: bengomo
cooked: <p>I’m trying to write a custom loss function with some specific logic and having a hard time coming up with a reformulation/approximation of that logic that is differentiable. I would love any help or suggestions that anyone may have on how I might proceed.</p>
<p>The loss function calculates how much money is gained or lost from betting on games. The logic I’ve implemented (but can’t figure out how to reformulate/approximate as differentiable) is: The model can predict three choices <code>[no_bet, bet_on_home, bet_on_away]</code>. Predicting <code>no_bet</code> adds zero to the loss. Incorrectly predicting to bet on home or away adds <code>+1</code> to the loss. Correctly predicting to bet on home or away subtracts <code>odds - 1</code> for that team from the loss.</p>
<p>For example, for a model <code>output</code>, target <code>y</code> (where 0 is a home win and 1 is an away win), and list of <code>odds</code> (column 0 is odds on home win and column 1 is odds on away win)</p>
<pre><code class="lang-auto">output: tensor([[ 0.2370,  0.2679,  0.3858],
        [ 0.0401,  0.5379, -0.1788],
        [-0.0537,  0.4639, -0.6986],
        [-0.0818,  0.6218, -0.5953],
        [ 0.1292,  0.1033, -0.1198],
        [-0.2797, -0.4663, -0.4223],
        [ 0.4070,  0.8124, -0.3414],
        [-0.5048,  1.0760,  0.3167]], device='cuda:0',
       grad_fn=&lt;AddmmBackward0&gt;)
y: tensor([1, 0, 1, 0, 0, 0, 0, 1], device='cuda:0')
odds: tensor([[2.3000, 1.6667],
        [2.3000, 1.6667],
        [1.9091, 1.9091],
        [1.1429, 6.0000],
        [2.2000, 1.7143],
        [1.8333, 2.0000],
        [1.0905, 8.0500],
        [5.0000, 1.2000]])
</code></pre>
<p>The following loss functions (which are NOT differentiable, from the argmax and other logic) are an example of the logic I described above.</p>
<pre><code class="lang-auto">def betting_loss(output, y, odds):
  # apply softmax
  whichbets = torch.argmax(torch.exp(F.log_softmax(output, dim = 1)), dim = 1)
  
  # keep only the games with bets places (not no_bet)
  betgames = whichbets != 0
  # also convert whichbets to [0,1] scale of y by subtracting 1 (2 - &gt; 1, and 1 -&gt; 0)
  whichbets = whichbets[betgames] - 1
  y = y[betgames]
  odds = odds[betgames]
  # games won
  wins = whichbets == y
  
  losses_loss = torch.sum(~wins)
  wins_loss = torch.sum(odds[wins, y[wins]] - 1)
  
  return losses_loss - wins_loss
</code></pre>
<p>The same thing, just in loop form for easier comprehension</p>
<pre><code class="lang-auto">def betting_loss_loop(output, y, odds):
  # apply softmax
  whichbets = torch.argmax(torch.exp(F.log_softmax(output, dim = 1)), dim = 1)
  # also convert whichbets to [0,1] scale of y by subtracting 1 (2 - &gt; 1, and 1 -&gt; 0)
  whichbets = whichbets - 1

  loss = 0
  for ii, whichbet in enumerate(whichbets):
    if whichbet != -1:
      if whichbet != y[ii]:
        loss += 1
      else:
        loss -= odds[ii, y[ii]] - 1

  return loss
</code></pre>
<pre><code class="lang-auto">print(betting_loss(output, y, odds))
print(betting_loss_loop(output, y, odds))
</code></pre>
<p>gives</p>
<pre><code class="lang-auto">tensor(-0.2000, device='cuda:0')
tensor(-0.2000)
</code></pre>
<p>Any help would be greatly appreciated. Thank you</p>