Skip to content

Commit 23caecf

Browse files
authored
Make NN training robust to NaN (#237)
1 parent f457259 commit 23caecf

1 file changed

Lines changed: 34 additions & 5 deletions

File tree

ml/Neural_Net_Classes.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
2655
class 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

Comments
 (0)