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.
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 [no_bet, bet_on_home, bet_on_away]. Predicting no_bet adds zero to the loss. Incorrectly predicting to bet on home or away adds +1 to the loss. Correctly predicting to bet on home or away subtracts odds - 1 for that team from the loss.
For example, for a model output, target y (where 0 is a home win and 1 is an away win), and list of odds (column 0 is odds on home win and column 1 is odds on away win)
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=<AddmmBackward0>)
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]])

The following loss functions (which are NOT differentiable, from the argmax and other logic) are an example of the logic I described above.
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 - > 1, and 1 -> 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

The same thing, just in loop form for easier comprehension
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 - > 1, and 1 -> 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

print(betting_loss(output, y, odds))
print(betting_loss_loop(output, y, odds))

gives
tensor(-0.2000, device='cuda:0')
tensor(-0.2000)

Any help would be greatly appreciated. Thank you