@@ -97,9 +97,12 @@ def __init__(
9797 self .compute_values = nn .Linear (
9898 self .max_encoder_length , self .max_encoder_length
9999 ) # noqa: E501
100+ # Emit a separate forecast head per quantile so each has its own
101+ # parameters and gradients (unlike tiling a point forecast with expand).
100102 self .linear_forecaster = nn .Linear (
101- self .max_encoder_length , self .max_prediction_length
102- ) # noqa: E501
103+ self .max_encoder_length ,
104+ self .max_prediction_length * self .n_quantiles ,
105+ )
103106
104107 def _scaled_dot_product_attention (
105108 self ,
@@ -169,18 +172,15 @@ def forward(self, x: dict[str, torch.Tensor]) -> dict[str, torch.Tensor]:
169172
170173 out = x_norm + att_score
171174 out = self .linear_forecaster (out )
175+ # (batch, channels, prediction_length * n_quantiles)
176+ batch_size , n_channels , _ = out .shape
177+ out = out .view (
178+ batch_size ,
179+ n_channels ,
180+ self .max_prediction_length ,
181+ self .n_quantiles ,
182+ )
183+ # Target is the last channel: (batch, prediction_length, n_quantiles)
184+ target_predictions = out [:, - 1 , :, :]
172185
173- out = out .transpose (1 , 2 )
174-
175- target_predictions = out [:, :, - 1 ] # (batch_size, max_prediction_length)
176-
177- if target_predictions .ndim == 1 :
178- target_predictions = target_predictions .unsqueeze (0 )
179-
180- if self .n_quantiles > 1 :
181- target_predictions = target_predictions .unsqueeze (- 1 ).expand (
182- - 1 , - 1 , self .n_quantiles
183- )
184- elif self .n_quantiles == 1 :
185- target_predictions = target_predictions .unsqueeze (- 1 )
186186 return {"prediction" : target_predictions }
0 commit comments