Repository navigation
Conversation
PierreQuinton
left a comment
There was a problem hiding this comment.
Good catch, however I feel a bit unconfortable casting the weights everytime. Should we copy the weights and cast/return the copy instead?
|
Thanks! If you'd rather not pay that conversion on every call for a mismatched matrix, I can cache the converted tensor per |
|
I'm not completely convinced about the cast to be honest. It feels like if this happens then the user most likely did something wrong, and hiding the mistake might be the best things. Note that as I can't really decide what is a good way to handle this, @ValerianRey @KhusPatel4450 any opinion? |
|
I think I agree with Pierre. This is more of a user mistake than a real issue IMO. Maybe we could improve error messages though. |
|
Fair point, a silent cast would hide the user's mistake. One thing I noticed though: I can rework this PR to register the weights as a buffer and raise an informative error in |
I agree that weights should be registered as buffers (not parameters since they're not trainable). Maybe even as buffers not affecting the state dict: self.register_buffer('foo', foo, persistent=False)I'm not entirely sure about that Also, it would be nice to do that not only for weights, but for every tensor that we need to move with a call to Feel free to do that in a new PR. thanks in advance! :) |
Problem
ConstantWeighting.forwardreturns the storedweightstensor as-is, without considering the dtype and device of the input matrix. The aggregators that combine the rows of the Jacobian with these weights directly fail with aRuntimeErroras soon as the weights and the Jacobian differ in dtype or device:Constant(weights @ matrixinWeightedAggregator._combine);ConFIGwith apref_vector(torch.linalg.pinv(units) @ weights);AlignedMTLwith apref_vector(B @ weightsinAlignedMTLWeighting).A typical case is a
pref_vectorcreated withtorch.tensor([...])(sofloat32, on CPU) used with a model infloat64, or on CUDA.UPGradandDualProjonly work by accident, because their projector goes through numpy and casts its result back to the dtype and device of the Gramian. The other weightings that hold a user-provided vector already cast it at use (SDMGradWeighting._resolve_w_tildeandPCDWeighting._get_taus), soConstantWeightingis the only one left behaving differently.Repro (CPU):
On
main:The same happens with a
float64pref_vectorand afloat32matrix, and with a CPUpref_vectorand a CUDA matrix (Expected all tensors to be on the same device).Solution
In
ConstantWeighting.forward, returnself.weights.to(dtype=matrix.dtype, device=matrix.device)instead ofself.weights. When the weights already match the matrix (the usual case),.toreturns the same tensor, so nothing changes; the storedweightsattribute is left untouched. With this change the repro prints:Tests
test_weights_dtype_differs_from_matrix_dtypeintests/unit/aggregation/test_constant.pyandtest_pref_vector_dtype_differs_from_matrix_dtypeintests/unit/aggregation/test_config.py, both parametrized over (float32,float64) and (float64,float32) for the weights and the matrix. The weights are created explicitly on CPU, so on a CUDA run (PYTEST_TORCH_DEVICE=cuda:0) the tests also cover the device mismatch. All 4 parametrizations fail onmainwith the errors above and pass with the fix.pytest tests/unit/aggregation -W error: 2281 passed, 2 skipped (CPU,float32); the two modified modules also pass underPYTEST_TORCH_DTYPE=float64.pytest tests/unit/autojac/test_jac_to_grad.py -W errorpasses.ruff check,ruff format --checkandty checkare unchanged.Added an entry under
[Unreleased] / Fixedin the changelog.