@@ -316,40 +316,69 @@ def parzen_windowing_gaussian(self, img: torch.Tensor) -> tuple[torch.Tensor, to
316316 Note: the input is expected to range between 0 and 1
317317 Args:
318318 img: the shape should be B[NDHW].
319+
320+ Returns:
321+ A tuple containing per-sample Gaussian bin weights and the
322+ corresponding marginal probability.
323+
324+ Raises:
325+ ValueError: if the Gaussian kernel buffers are unavailable.
319326 """
320- img = torch .clamp (img , 0 , 1 )
327+ output_dtype = img .dtype
328+ if output_dtype == torch .float16 :
329+ img = img .float ()
330+ compute_dtype = torch .float32 if img .dtype in (torch .float16 , torch .bfloat16 ) else img .dtype
331+ img = torch .clamp (img , 0 , 1 ).to (dtype = compute_dtype )
321332 img = img .reshape (img .shape [0 ], - 1 , 1 ) # (batch, num_sample, 1)
322333 if self .bin_centers is None or self .preterm is None :
323334 raise ValueError ("bin_centers and preterm must be defined for gaussian parzen windowing." )
324- weight = torch . exp (
325- - self .preterm . to ( img ) * ( img - self . bin_centers .to (img )) ** 2
326- ) # (batch, num_sample, num_bin)
335+ preterm = self . preterm . to ( device = img . device , dtype = compute_dtype )
336+ bin_centers = self .bin_centers .to (device = img . device , dtype = compute_dtype )
337+ weight = torch . exp ( - preterm * ( img - bin_centers ) ** 2 ) # (batch, num_sample, num_bin)
327338 weight = weight / torch .sum (weight , dim = - 1 , keepdim = True ) # (batch, num_sample, num_bin)
328339 probability = torch .mean (weight , dim = - 2 , keepdim = True ) # (batch, 1, num_bin)
340+ if output_dtype in (torch .float16 , torch .bfloat16 ):
341+ weight = weight .to (dtype = output_dtype )
342+ probability = probability .to (dtype = output_dtype )
329343 return weight , probability
330344
331345 def forward (self , pred : torch .Tensor , target : torch .Tensor ) -> torch .Tensor :
332346 """
333347 Args:
334348 pred: the shape should be B[NDHW].
335349 target: the shape should be same as the pred shape.
350+ Returns:
351+ Reduced negative mutual information loss.
352+
336353 Raises:
337354 ValueError: When ``self.reduction`` is not one of ["mean", "sum", "none"].
338355 """
339356 if target .shape != pred .shape :
340357 raise ValueError (f"ground truth has differing shape ({ target .shape } ) from pred ({ pred .shape } )" )
341358 wa , pa , wb , pb = self .parzen_windowing (pred , target ) # (batch, num_sample, num_bin), (batch, 1, num_bin)
342359
343- pab = torch .bmm (wa .permute (0 , 2 , 1 ), wb .to (wa )).div (wa .shape [1 ]) # (batch, num_bins, num_bins)
344- papb = torch .bmm (pa .permute (0 , 2 , 1 ), pb .to (pa )) # (batch, num_bins, num_bins)
345- mi = torch .sum (
346- pab * torch .log ((pab + self .smooth_nr ) / (papb + self .smooth_dr ) + self .smooth_dr ), dim = (1 , 2 )
347- ) # (batch)
360+ # Half-precision matrix multiplication can overflow before the joint
361+ # histogram is divided by the number of samples. Accumulate histogram
362+ # products in float32 while preserving float64 inputs.
363+ output_dtype = wa .dtype
364+ compute_dtype = torch .float32 if wa .dtype in (torch .float16 , torch .bfloat16 ) else wa .dtype
365+ wa = wa .to (dtype = compute_dtype )
366+ wb = wb .to (wa )
367+ pa = pa .to (dtype = compute_dtype )
368+ pb = pb .to (pa )
369+ with torch .autocast (device_type = wa .device .type , enabled = False ):
370+ pab = torch .bmm (wa .permute (0 , 2 , 1 ), wb ).div (wa .shape [1 ]) # (batch, num_bins, num_bins)
371+ papb = torch .bmm (pa .permute (0 , 2 , 1 ), pb ) # (batch, num_bins, num_bins)
372+ mi = torch .sum (
373+ pab * torch .log ((pab + self .smooth_nr ) / (papb + self .smooth_dr ) + self .smooth_dr ), dim = (1 , 2 )
374+ ) # (batch)
348375
349376 if self .reduction == LossReduction .SUM .value :
350- return torch .sum (mi ).neg () # sum over the batch and channel ndims
351- if self .reduction == LossReduction .NONE .value :
352- return mi .neg ()
353- if self .reduction == LossReduction .MEAN .value :
354- return torch .mean (mi ).neg () # average over the batch and channel ndims
355- raise ValueError (f'Unsupported reduction: { self .reduction } , available options are ["mean", "sum", "none"].' )
377+ loss = torch .sum (mi ).neg () # sum over the batch and channel ndims
378+ elif self .reduction == LossReduction .NONE .value :
379+ loss = mi .neg ()
380+ elif self .reduction == LossReduction .MEAN .value :
381+ loss = torch .mean (mi ).neg () # average over the batch and channel ndims
382+ else :
383+ raise ValueError (f'Unsupported reduction: { self .reduction } , available options are ["mean", "sum", "none"].' )
384+ return loss .to (dtype = output_dtype )
0 commit comments