-
Notifications
You must be signed in to change notification settings - Fork 24.3k
Allow __array__
to automatically detach and move to CPU
#36560
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
If people are ok with this I'd love to submit a PR. The only question I have is whether we should warn when detaching/CPUing — I would favour yes. |
I agree that a warning should be explicitly shown in such a case. |
Our previous stance has been that we will NOT silently convert tensors from cuda to cpu under any circumstances, because this can lead to silent and hard to debug performance problems. That being said, I am sympathetic to your desire to not have PyTorch specific code in your codebase. cc @mruberry |
I share @ezyang's thinking. This sounds like a significant and scary change for PyTorch to make. While I appreciate the desire to keep your code base as elegant as possible, would it really be so bad to tell users to transfer their tensors to CPU or NumPy first? That way the device synchronization would be explicit. |
Thanks all for your fast responses! If there were only PyTorch arrays and NumPy arrays, it would not be a big deal to add code or a note in the docs about how to deal with them. But there are also all the arrays mentioned above, plus Jax, CuPy, tensorflow, chainer, mxnet, pint Quantities, PyOpenCL, unyt, ... And more to come, I'm sure! In all these cases, converting to NumPy results in information loss, but it would be a nightmare to have to support all of the individual conversions. So the issue for me isn't really "how hard is it really to deal with PyTorch properly", but "how can we have some kind of conversion standards that all array libraries can agree to implement". For example, would adding a I'm curious whether @rgommers, @shoyer or @seberg would have comments here about the best way forward for the community as a whole. One proposal that could alleviate concerns is to simultaneously add support for I'd be happy to help (as much as I can, as a consumer of these libraries rather than a developer) push a solution forward that satisfies the PyTorch devs, but it should be a solution that is not PyTorch-specific. My view is that implicit conversion with loud warnings would be fine, but I sense that that would be an uphill battle up a very steep hill. 😂 One last option that might be a reasonable intermediate alternative: a Thanks again! np.min on torch.Tensor error>>> t = torch.zeros([2, 4])
>>> np.min(t)
TypeError Traceback (most recent call last)
<ipython-input-13-b148c1b25541> in <module>
----> 1 np.min(t)
<__array_function__ internals> in amin(*args, **kwargs)
~/conda/envs/pip7/lib/python3.7/site-packages/numpy/core/fromnumeric.py in amin(a, axis, out, keepdims, initial, where)
2791 """
2792 return _wrapreduction(a, np.minimum, 'min', axis, None, out,
-> 2793 keepdims=keepdims, initial=initial, where=where)
2794
2795
~/conda/envs/pip7/lib/python3.7/site-packages/numpy/core/fromnumeric.py in _wrapreduction(obj, ufunc, method, axis, dtype, out, **kwargs)
86 return reduction(axis=axis, dtype=dtype, out=out, **passkwargs)
87 else:
---> 88 return reduction(axis=axis, out=out, **passkwargs)
89
90 return ufunc.reduce(obj, axis, dtype, out, **passkwargs)
TypeError: min() received an invalid combination of arguments - got (out=NoneType, axis=NoneType, ), but expected one of:
* ()
* (name dim, bool keepdim)
didn't match because some of the keywords were incorrect: out, axis
* (Tensor other)
* (int dim, bool keepdim)
didn't match because some of the keywords were incorrect: out, axis |
The problem is that there's way too much code out in the wild (e.g., in SciPy or Scikit-Learn) that starts by casting inputs with It would indeed be nice to have a standard way to "hard convert" GPU/sparse/distributed arrays into NumPy arrays, it just can't be the default behavior of This would need to be blessed by discussion on the NumPy mailing list. Even better would be to write up a short NumPy Enhancement Proposal, with a discussion of the motivation and usage guidelines about when |
I agree with @shoyer plotting libraries are indeed a nice point where something like a "forced cast" is a reasonable thing to do (since e.g. you do not have a return value that might have the same type as the input). I wouldn't mind exploring what happens if you do things like implement In either case, a |
I think I agree with @ezyang and @mruberry here - there are advantages to being explicit, code does become clearer about what it does (e.g. Forced casting sounds like a nice convenience for a visualization library, however this can also be seen as a learning moment for users. If they have no idea about whether their data lives on a GPU or how to get it back to a CPU and what the cost of that is, there's bigger issues than having to type a few characters more ....
Why do you (as a viz library author) have to support this? Is it a matter of saving a user typing
I'm not sure about this statement. If you support CPU tensors, you are playing well with PyTorch.
don't do that please:) |
Well, to me it comes to the question of why we have protocols at all. Should the moment of visualisation be the moment at which every user learns the specifics of how to get a numpy array for that particular library they are using right then? I remember a few years ago, someone tweeted that adapting two FOSS libraries to work together was more akin to organ transplant than to snapping legos together. At the time, I remember thinking that I was so lucky to be working with scientific Python, where I didn't find this true at all. Because everybody spoke NumPy, I could plug scientific libraries together in minutes. It was not remotely like organ transplant. I started out in my career by mixing together NumPy, SciPy, scikit-image, scikit-learn, and NetworkX. It was just a brilliant experience. Now if I want to work with PyTorch and napari, things don't just work. I should learn v = napari.Viewer()
for result in results():
v.add_layer(result) In the non- I think this is not a small thing, it's a huge thing, and it's only going to get bigger as the array landscape continues to evolve. Anyway, I hope that's a good enough justification to consider >>> np.asarray(torch_with_grad)
# ---> Error
>>> np.asarray(torch_on_gpu)
# ---> Error
>>> np.asarray(torch_with_grad, force=True)
# ---> array!
>>> np.asarray(torch_on_gpu, force=True)
# ---> array! Thoughts? @rgommers btw, I'm actually dismayed to find out that |
We would consider supporting a "force to NumPy" option in PyTorch. See #20778. Would that address your concern? |
Well, I am not sure that adding an option to pytorch can really help with the issue Juan is pointing out. Let me try to unravel this:
So, should napari do But, Juan's argument remains. Even if you say that napari should never use a potential EDIT: To be fair, if we discourage napari to do the conversion, the pressure to actually provide a protocol is much lower, so an argument such that it adds annoying API to NumPy is much stronger. |
Does this hypothetical user of multiple 8000 tensor libraries actually exist? It seems like we're trying to cater to the following niche:
Does it really take that much time to look up |
The discussion on adding a This discussion has more detail than gh-13568, so I'll close that as a duplicate. Adding a |
The That's as much as can be done here I think, until NumPy adds a |
Uh oh!
There was an error while loading. Please reload this page.
🚀 Feature
I would like
__array__
to always implicitly detach and transfer to CPU before returning a numpy array, so thatnp.asarray(mytensor)
is guaranteed to work.Motivation
For good reasons detailed in this Discourse thread, a
torch.Tensor
with gradients needs to be.detach()
ed before it is converted to NumPy, and further, if the Tensor is on the GPU it needs to be explicitly transferred back. Specifically:As someone not very familiar with
requires_grad
, I am fully on board with this. 😅However, the purpose of
__array__
is to allow functions to be written against a unique API (NumPy) to work on arrays of other types without having to know anything about said array. Having to go through.detach()[.cpu()]
breaks this assumption.Pitch
The specific use case I have in mind is viewing tensors. In napari, we pass input arrays through
__getitem__
and then throughnp.asarray
, which lets us lazily view n-dimensional arrays as long as they satisfy the__array__
API. This works for NumPy, dask, zarr, and Xarray, but I stumbled when I was trying to demo it with torch Tensors. You can see a brief demo in this noise2self notebook:https://github.com/jni/noise2self/blob/napari/notebooks/Intro%20to%20Neural%20Nets.ipynb
(You need the usual suspects, plus
pip install napari
orconda install -c conda-forge napari
for the viewer.)Alternatives
Things could remain as is, and there are advantages (articulated in the Discourse post). However, this means that anyone else in the NumPy ecosystem that wants to play well with
torch.Tensor
s then needs to include PyTorch-specific code in their code base, which is not super nice. (Or monkey-patch it, which is also not super nice!)Additional context
original
__array__
proposal: #2935original
__array__
implementation: #2945possibly related issue: #9648
I couldn't find too much discussion on the issues or PRs about the decision not to
.detach()
automatically in__array__
.cc @ngimel
The text was updated successfully, but these errors were encountered: