You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug] LocalNormalizedCrossCorrelationLoss: kernel not registered as buffer — silent gradient tracking + wrong device placement #8819
In monai/losses/image_dissimilarity.py, LocalNormalizedCrossCorrelationLoss.__init__ contains two related problems in kernel initialization:
Problem 1: Typo in require_grads (silent no-op)
self.kernel=_kernel(self.kernel_size)
self.kernel.require_grads=False# BUG: 'require_grads' is NOT a valid tensor attribute!self.kernel_vol=self.get_kernel_vol()
require_grads (plural, with trailing s) is not a valid PyTorch tensor attribute. The correct attribute is requires_grad. This line silently does nothing — it creates a new Python attribute called require_grads on the tensor object instead of controlling gradient tracking. As a result, the kernel tensor silently tracks gradients in every forward pass, consuming unnecessary memory in the computation graph.
Problem 2: Plain attribute assignment instead of register_buffer
Using self.kernel = ... (plain attribute) instead of self.register_buffer("kernel", ...) means:
When the user calls loss.to("cuda"), loss.cuda(), or loss.half() on the loss module, the kernel does NOT move to the target device — it stays on CPU. This causes a device mismatch at runtime.
The kernel is not included in state_dict() / load_state_dict() which leads to silent inconsistencies if checkpointing the loss object.
To Reproduce
importtorchfrommonai.lossesimportLocalNormalizedCrossCorrelationLossloss=LocalNormalizedCrossCorrelationLoss(spatial_dims=3, kernel_type="gaussian")
# Bug 1: require_grads typo silently does nothingprint(loss.kernel.requires_grad) # True! Not False as intendedprint(hasattr(loss.kernel, 'require_grads')) # True -- spurious attribute created# Bug 2: kernel not a registered bufferprint(dict(loss.named_buffers())) # {} -- kernel is NOT here!loss.cuda()
print(loss.kernel.device) # cpu -- kernel did NOT move to GPU!
Expected behavior
loss.kernel.requires_grad should be False
loss.kernel and loss.kernel_vol should appear in loss.named_buffers()
After loss.cuda(), loss.kernel.device should be cuda:0
I've submitted a PR implementing this fix for GlobalMutualInformationLoss (the remaining case mentioned in the issue, after #8818 addressed LocalNormalizedCrossCorrelationLoss): #8872. Please review.
Activity
ericspod commentedon May 20, 2026
I believe this issue has been closed by #8818 so closing now, but re-open if needed.
AlexanderSanin commentedon May 25, 2026
I've submitted a PR implementing this fix for
GlobalMutualInformationLoss(the remaining case mentioned in the issue, after #8818 addressedLocalNormalizedCrossCorrelationLoss): #8872. Please review.fix(losses): register buffers in GlobalMutualInformationLoss
fix(losses): register buffers in GlobalMutualInformationLoss
fix(losses): register buffers in GlobalMutualInformationLoss (#8872)