@@ -23,6 +23,35 @@ def __call__(self, val_loss):
2323 self .early_stop = True
2424
2525
26+ def nan_mse_loss ( target , pred ):
27+ """
28+ Custom MSE loss that ignores NaN values in targets
29+ (Here, NaN often correspond to missing values in the target data)
30+
31+ Args:
32+ target: target values (may contain NaN)
33+ pred: predicted values
34+
35+ Returns:
36+ mean squared error ignoring NaN values
37+ """
38+ # Compute squared differences
39+ squared_diff = (pred - target ) ** 2
40+
41+ # Use nanmean to ignore NaN values
42+ mse_loss = torch .nanmean (squared_diff )
43+
44+ # Prevent NaN from contaminating backpropagation
45+ # See https://github.com/pytorch/pytorch/issues/4132
46+ if pred .requires_grad :
47+ nan_mask = torch .isnan (squared_diff )
48+ def mask_grad_hook (grad ):
49+ return torch .where (nan_mask , 0 , grad )
50+ pred .register_hook (mask_grad_hook )
51+
52+ return mse_loss
53+
54+
2655class CombinedNN (nn .Module ):
2756 """
2857 Model that trains a 5 layer neural network and a calibration layer
@@ -52,7 +81,7 @@ def __init__(self, input_size, output_size, hidden_size=20,
5281 self .sim_to_exp_calibration_weight = nn .Parameter (torch .ones (output_size ))
5382 self .sim_to_exp_calibration_bias = nn .Parameter (torch .zeros (output_size ))
5483
55- self . criterion = nn .MSELoss ()
84+ # Use custom loss function instead of nn.MSELoss()
5685 self .optimizer = optim .Adam (self .parameters (), lr = learning_rate )
5786 self .scheduler = ReduceLROnPlateau (self .optimizer , 'min' ,
5887 factor = factor , patience = patience_LRreduction , threshold = threshold )
@@ -91,10 +120,10 @@ def train_model(self, sim_inputs, sim_targets,
91120 loss = 0
92121 if len (sim_inputs ) > 0 :
93122 sim_outputs = self (sim_inputs )
94- loss += self . criterion ( sim_targets , sim_outputs )
123+ loss += nan_mse_loss ( sim_targets , sim_outputs )
95124 if len (exp_inputs ) > 0 :
96125 exp_outputs = self .calibrate ( self (exp_inputs ) )
97- loss += self . criterion ( exp_targets , exp_outputs )
126+ loss += nan_mse_loss ( exp_targets , exp_outputs )
98127 loss .backward ()
99128
100129 self .optimizer .step ()
@@ -107,10 +136,10 @@ def train_model(self, sim_inputs, sim_targets,
107136 val_loss = 0
108137 if len (sim_inputs_val ) > 0 :
109138 sim_outputs_val = self (sim_inputs_val )
110- val_loss += self . criterion ( sim_targets_val , sim_outputs_val )
139+ val_loss += nan_mse_loss ( sim_targets_val , sim_outputs_val )
111140 if len (exp_inputs_val ) > 0 :
112141 exp_outputs_val = self (exp_inputs_val )
113- val_loss += self . criterion ( exp_targets_val , exp_outputs_val )
142+ val_loss += nan_mse_loss ( exp_targets_val , exp_outputs_val )
114143
115144
116145 if (epoch + 1 ) % (num_epochs / 10 ) == 0 :
0 commit comments