Fastai Regression model with observation weight
Is it possible to have a costume mean squared error function with sample weight for each observation?
I am able to utilize the standard fastai training loop and I am able to implement this costume loss in PyTorch.
How to put that to fastai learner object on tabular data?
I know keras has this already implemented in the .fit method where sample_weight argument present.
def weighted_mse_loss(input, target, weight):
    return torch.sum(weight * (input - target) ** 2)

from fastai.tabular.all import *
import seaborn as sns

df = sns.load_dataset('tips')
df = df.assign(sample_weight = np.random.normal(size = df.shape[0], loc = 10, scale = 2))

y = ['total_bill']
cont = ['tip']
cat = ['sex', 'smoker', 'day', 'time', 'size']

procs = [Normalize, Categorify]

df[&quot;Y&quot;] = np.log(df[y] + 1)

MIN = df[&quot;Y&quot;].min()
MAX = df[&quot;Y&quot;].max()

splits =  RandomSplitter(valid_pct=0.2)(range_of(df))

to = TabularPandas(
    df,
    procs=procs,
    cat_names=cat,
    cont_names=cont,
    y_names=&quot;Y&quot;,
    splits=splits,
    y_block=RegressionBlock(n_out = 1),
)

dls = to.dataloaders(
    bs=64, shuffle_train=True
)

config = tabular_config(
        embed_p=0.05, 
        y_range=[0, MAX * 1.1],
        bn_final=False,
        ps=[0.05, 0.05, 0.05],
    )

learner = tabular_learner(
        dls,
        layers=[1000, 500, 250],
        config=config,
        wd=0.2,
        metrics=[rmse,],
    )

learner.fit_one_cycle(40, lr_max = 0.01,
                          wd = 0.1)


I am using this workaround:

in y_names for TabularPandas, you can return the tuple of (weight, y) as
to = TabularPandas(df,
                   procs=procs,
                   cat_names=cat,
                   cont_names=cont,
                   y_names=[&quot;sample_weight&quot;,&quot;Y&quot;],
                   splits=splits,
                   y_block=RegressionBlock(n_out = 1))


In your loss function, split your target to (weights, target) and apply weights to loss, e.g.:
 class SampleWeightedCE(torch.nn.modules.loss._Loss):
     def __init__(self):
         super(SampleWeightedCE, self).__init__()
         self.ce_loss = torch.nn.BCEWithLogitsLoss(reduction='none')

 def forward(self, output, tgt):       
     weights = tgt[:,0].unsqueeze(1)
     target = tgt[:,1].unsqueeze(1)            

     losses = self.ce_loss(output, target) * weights
     return torch.sum(losses) / torch.sum(weights)


If you want to measure metrics, you can use the same workaround, such as:
def accuracy_W(inp, tgt, thresh=0.5, sigmoid=True):
 weights = tgt[:,0].unsqueeze(1)
 target = tgt[:,1].unsqueeze(1)

 if sigmoid: inp = inp.sigmoid()    
 classes = (inp &gt;= thresh)
 m_target = (target &gt;= 0.5)
 correct = (m_target == classes) 
 return torch.sum(weights * correct) / torch.sum(weights)  


In get_preds() or predict(), you need to split the target
 y_prob, y_out = learn.get_preds(ds_idx=1, with_input=False, with_loss=False, reorder=False)  
 weights = y_out[:,0]
 target = y_out[:,1]



