Similar to the legendary post for XGBoost

I try to implement a custom loss function for lightgbm. My problem is that it always returns NaN as the predictions it gets passed are always 0. I am looking for any hint what the problem might be. The following code should be executable if you have a utils.read_training_data or obtain the train dataframe otherwise.

```
import lightgbm
import numpy as np
import pandas as pd
from utils import read_training_data
import torch
from torch.autograd import grad
train = read_training_data()
era_idx = [np.where(train.erano==uera)[0] for uera in train.erano.unique()]
features = [c for c in train.columns if c.startswith('feature_')]
# define adjusted sharpe in terms of cost adjusted numerai sharpe
def numerai_sharpe(x):
return (x.mean() -0.010415154) / (x.std()+1)
def skew(x):
mx = x.mean()
m2 = ((x-mx)**2).mean()
m3 = ((x-mx)**3).mean()
return m3/(m2**1.5)
def kurtosis(x):
mx = x.mean()
m4 = ((x-mx)**4).mean()
m2 = ((x-mx)**2).mean()
return (m4/(m2**2))-3
def adj_sharpe(x):
return numerai_sharpe(x) * (1 + ((skew(x) / 6) * numerai_sharpe(x)) - ((kurtosis(x) / 24) * (numerai_sharpe(x) ** 2)))
# use correlation as the measure of fit
def corr(pred, target):
pred_n = pred - pred.mean(dim=0)
pred_n = pred_n / pred_n.norm(dim=0)
target_n = target - target.mean(dim=0)
target_n = target_n / target_n.norm(dim=0)
l = torch.matmul(pred_n, target_n)
return l
def lgbm_train_fobj(preds, train_data):
# convert to pytorch tensors
ypred_th = torch.tensor(preds, requires_grad=True)
ytrue_th = torch.tensor(train_data.get_label().astype(float))
all_corrs = []
# get correlations in each era
for ee in era_idx:
score = corr(ypred_th[ee], ytrue_th[ee])
all_corrs.append(score)
all_corrs = torch.stack(all_corrs)
# calculate adjusted sharpe using correlations
loss = -adj_sharpe(all_corrs)
print(f'Current loss:{loss}')
# calculate gradient and convert to numpy
loss_grads = grad(loss, ypred_th, create_graph=True)[0]
loss_grads = loss_grads.detach().numpy()
# return gradient and ones instead of Hessian diagonal
return loss_grads, np.ones(loss_grads.shape)
def lgbm_train_eval(preds, train_data):
ypred_th = torch.tensor(preds, requires_grad=True)
ytrue_th = torch.tensor(train_data.get_label().astype(float))
all_corrs = []
# get correlations in each era
for ee in era_idx:
score = corr(ypred_th[ee], ytrue_th[ee])
all_corrs.append(score)
all_corrs = torch.stack(all_corrs)
return 'corr', all_corrs.mean(), True
bster = lightgbm.train(
{
'max_depth':5,
'learning_rate':0.01,
'colsample_bytree':0.1,
'num_leaves':32,
'random_state':666,
},
lightgbm.Dataset(train[features], label=train['target']),
num_boost_round=10,
fobj=lgbm_train_fobj,
feval = lgbm_train_eval
)
```

The output reads:

```
[LightGBM] [Warning] Using self-defined objective function
[LightGBM] [Warning] Auto-choosing row-wise multi-threading, the overhead of testing was 0.002556 seconds.
You can set `force_row_wise=true` to remove the overhead.
And if memory is not enough, you can set `force_col_wise=true`.
[LightGBM] [Info] Total Bins 5250
[LightGBM] [Info] Number of data points in the train set: 21177, number of used features: 1050
[LightGBM] [Warning] Using self-defined objective function
Current loss:nan
[LightGBM] [Warning] No further splits with positive gain, best gain: -inf
[LightGBM] [Warning] Stopped training because there are no more leaves that meet the split requirements
Current loss:nan
(repeated, so omitted)
```

Thank you very much!