@@ -68,13 +68,13 @@ def _infer_mode(loss: nn.Module) -> _Mode:
6868
6969 Returns
7070 -------
71- { "point", "class", "gaussian_nll"}
71+ Literal[ "point", "class", "gaussian_nll"]
7272 ``"class"`` for ``CrossEntropyLoss`` / ``NLLLoss``,
7373 ``"gaussian_nll"`` for ``GaussianNLLLoss``, else ``"point"``.
7474 """
7575 if isinstance (loss , _CLASS_LOSSES ):
7676 return "class"
77- if isinstance (loss , _GAUSSIAN_NLL_LOSSES ):
77+ elif isinstance (loss , _GAUSSIAN_NLL_LOSSES ):
7878 return "gaussian_nll"
7979 return "point"
8080
@@ -112,13 +112,46 @@ def loss(self, y_pred: torch.Tensor, target: torch.Tensor) -> torch.Tensor:
112112
113113 if mode == "class" :
114114 return per_elem .view (batch_size , time_idx )
115- if per_elem .ndim == 0 :
115+ elif per_elem .ndim == 0 :
116116 # defensive: some losses ignore reduction="none"
117117 return per_elem .expand (batch_size , time_idx )
118118 return per_elem
119119
120- def update (self , y_pred , target ):
121- """Update metric state; reject multi-target lists with a clear error."""
120+ def update (
121+ self ,
122+ y_pred : torch .Tensor | list [torch .Tensor ],
123+ target : torch .Tensor
124+ | tuple [torch .Tensor , torch .Tensor | None ]
125+ | tuple [list [torch .Tensor ], torch .Tensor | None ],
126+ ) -> None :
127+ """Accumulate batch loss into metric state.
128+
129+ Parameters
130+ ----------
131+ y_pred : torch.Tensor or list of torch.Tensor
132+ Network prediction for a single target.
133+
134+ * point: ``[B, T, 1]`` or ``[B, T]``
135+ * class: ``[B, T, C]`` logits
136+ * gaussian_nll: ``[B, T, 2]`` as ``(mean, raw_variance)``
137+
138+ A list of prediction tensors is not supported; use
139+ :class:`~pytorch_forecasting.metrics.MultiLoss` for multi-target.
140+ target : torch.Tensor or tuple
141+ Ground truth. Either a tensor ``[B, T]``, or
142+ ``(target, weight)`` where ``weight`` is ``[B, T]`` or ``None``.
143+ A list of target tensors is not supported.
144+
145+ Returns
146+ -------
147+ None
148+
149+ Raises
150+ ------
151+ ValueError
152+ If ``target`` (or the first element of ``(target, weight)``) is a
153+ list of tensors.
154+ """
122155 # MultiHorizonMetric unpacks (target, weight) before calling loss();
123156 # catch list targets here so the message is useful.
124157 raw_target = target
@@ -164,7 +197,7 @@ def _prepare_inputs(
164197 if mode == "point" :
165198 if y_pred .ndim != 3 :
166199 return y_pred , target
167- if y_pred .size (- 1 ) != 1 :
200+ elif y_pred .size (- 1 ) != 1 :
168201 raise ValueError (
169202 "Error inNNLossAdapter for point prediction (H=1): "
170203 f"Got y_pred shape { list (y_pred .shape )} with "
@@ -181,25 +214,27 @@ def _prepare_inputs(
181214 "Classification losses expect logits of shape "
182215 f"(batch, time, classes), got { tuple (y_pred .shape )} ."
183216 )
184- if target .ndim != 2 :
217+ elif target .ndim != 2 :
185218 raise ValueError (
186219 "Classification targets must have shape (batch, time), "
187220 f"got { tuple (target .shape )} ."
188221 )
189222 return y_pred .reshape (- 1 , y_pred .size (- 1 )), target .reshape (- 1 ).long ()
190223
191224 # gaussian_nll
192- if y_pred .ndim != 3 or y_pred .size (- 1 ) != 2 :
193- raise ValueError (
194- "GaussianNLLLoss expects predictions of shape "
195- f"(batch, time, 2) as (mean, raw_variance); got { tuple (y_pred .shape )} ."
196- )
197- if target .ndim != 2 :
198- raise ValueError (
199- "GaussianNLL targets must have shape (batch, time), "
200- f"got { tuple (target .shape )} ."
201- )
202- return y_pred , target
225+ else :
226+ if y_pred .ndim != 3 or y_pred .size (- 1 ) != 2 :
227+ raise ValueError (
228+ "GaussianNLLLoss expects predictions of shape "
229+ "(batch, time, 2) as (mean, raw_variance), "
230+ f"got { tuple (y_pred .shape )} ."
231+ )
232+ elif target .ndim != 2 :
233+ raise ValueError (
234+ "GaussianNLL targets must have shape (batch, time), "
235+ f"got { tuple (target .shape )} ."
236+ )
237+ return y_pred , target
203238
204239 def _call_loss (
205240 self ,
@@ -233,7 +268,7 @@ def _call_loss(
233268 mean = y_pred [..., 0 ]
234269 var = F .softplus (y_pred [..., 1 ]) + 1e-6
235270 return self ._loss (mean , target , var )
236- if mode == "class" and isinstance (self ._loss , nn .NLLLoss ):
271+ elif mode == "class" and isinstance (self ._loss , nn .NLLLoss ):
237272 # NLLLoss expects log-probabilities
238273 return self ._loss (F .log_softmax (y_pred , dim = - 1 ), target )
239274 return self ._loss (y_pred , target )
@@ -261,9 +296,9 @@ def to_prediction(self, y_pred: torch.Tensor, **kwargs) -> torch.Tensor:
261296 mode = self ._mode
262297 if mode == "class" and y_pred .ndim == 3 :
263298 return y_pred .argmax (dim = - 1 )
264- if mode == "gaussian_nll" and y_pred .ndim == 3 and y_pred .size (- 1 ) == 2 :
299+ elif mode == "gaussian_nll" and y_pred .ndim == 3 and y_pred .size (- 1 ) == 2 :
265300 return y_pred [..., 0 ]
266- if y_pred .ndim == 3 and y_pred .size (- 1 ) == 1 :
301+ elif y_pred .ndim == 3 and y_pred .size (- 1 ) == 1 :
267302 return y_pred .squeeze (- 1 )
268303 return y_pred
269304
0 commit comments