Add per-element unique op for CPU#5503
Conversation
|
On your questions:
|
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
Look great, had mostly nits. then in the current See here for an example. EDIT: This is a better example for CPU: https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/TensorCompare.cpp |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
ssnl
left a comment
There was a problem hiding this comment.
This at its current state still needs more work:
- Documentation at
_torch_docs.pyand_tensor_docs.py - Dispatch to other types can be in a later PR, but this will fail with cuda long tensor (probably a segfault). Can you add a cuda version as well?
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
I think you accidentally merged instead of rebasing |
|
Fixed merge. Documentations added and exposed unique as function as well. In order to do so, it turns out using On further thought, realized that there's not really a sensible way to do gradients for this, so explicitly declared it as not implemented. Regarding CUDA support, @ssnl - I pinged you, but it seems you're off the grid? In any case, I'd probably prefer to defer that - so is there a way to explicitly warn/catch CUDA usage for more graceful failure than segfault? |
| #include <unordered_map> | ||
| #include <unordered_set> | ||
|
|
||
| #include <iostream> |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| if (sorted) { | ||
| std::vector<scalar_t> vec(set.begin(), set.end()); | ||
| std::sort(vec.begin(), vec.end()); | ||
| std::copy(vec.begin(), vec.end(), output->data<scalar_t>()); |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| self.assertEqual(empty_inverse, x_inverse) | ||
|
|
||
| x_unique, x_inverse = torch.autograd.Variable.unique( | ||
| x, sorted=True, return_inverse=True) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| return_inverse=True, | ||
| ) | ||
| self.assertEqual(torch.ByteTensor([7, 42, 128, 133]), byte_unique) | ||
| self.assertEqual(torch.LongTensor([3, 0, 0, 0, 1, 2]), byte_inverse) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| - **output** (*Tensor*): the list of unique scalar elements | ||
| - **inverse_indices** (*Tensor*): the indices (same shape as input) | ||
| for where elements in the original input map to in the output | ||
| if ``return_inverse`` is ``True``; otherwise, an empty tensor. |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
@theweiho about gradients for |
|
@ssnl - is there some way to use Travis CI (or something else) to test the CUDA part of the code you suggested? @fmassa - that was the way we were considering doing, but not sure that totally makes sense. Attributing the gradient to the first unique element seems a bit arbitrary? (Considering all occurrences of that element "contributes" to the unique equally) I also wasn't sure what a use case for the unique gradient would be, so figured it may make sense to defer it until someone has concrete requirements. |
| const bool return_inverse, | ||
| Tensor* output, | ||
| Tensor* inverse_indices) { | ||
| set_type<scalar_t> set( |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
|
||
| if (sorted) { | ||
| AT_DISPATCH_ALL_TYPES(self.type(), "unique", [&] { | ||
| _unique_cpu_template<std::set, scalar_t>( |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| throw std::runtime_error( | ||
| "unique is currently CPU-only, and lacks CUDA support. " | ||
| "Pull requests welcome!"); | ||
| return std::make_tuple(self.type().tensor({0}), self.type().tensor({0})); |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| } | ||
| for (int i = 0; i < input.numel(); ++i) { | ||
| inverse_indices.data<int64_t>()[i] = | ||
| inverse_map[input.data<scalar_t>()[i]]; |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| const Tensor& input = self.contiguous(); | ||
| set_type<scalar_t> set( | ||
| input.data<scalar_t>(), input.data<scalar_t>() + input.numel()); | ||
| Tensor output = input.type().tensor({static_cast<long long>(set.size())}); |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| expected_unique.tolist(), sorted(x_unique.tolist())) | ||
| self.assertEqual(empty_inverse, x_inverse) | ||
|
|
||
| x_unique, x_inverse = x.unique(return_inverse=True) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| #include "ATen/ATen.h" | ||
|
|
||
| #include <tuple> | ||
| A |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
@pytorchbot add to whitelist |
|
@pytorchbot retest this please |
2 similar comments
|
@pytorchbot retest this please |
|
@pytorchbot retest this please |
…o returning a 0-length tensor, per off-line reviewer comments
| x_unique, x_inverse = x.unique(return_inverse=True) | ||
| self.assertEqual( | ||
| expected_unique.tolist(), sorted(x_unique.tolist())) | ||
| self.assertEqual(expected_inverse.numel(), x_inverse.numel()) |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
ezyang
left a comment
There was a problem hiding this comment.
I don't think the tests in the hashing case are correct.
Questions/possible future works: How to template-ize to extend support beyond LongTensor? How to check if autograd works (and if not, how to add explicit gradient)? CUDA support? Testing command: DEBUG=1 NO_CUDA=1 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py build && DEBUG=1 NO_CUDA=1 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py develop && python3 test/test_torch.py Partially fixes pytorch#2031 * Initial commit for unique op * Working unique with test * Make inverse indices shape conform to input * flake8 whitespace removal * address review comment nits * Expose fn and add docs. Explicitly declare no gradients * Trial generic dispatch implementation * Add tests for generics * flake8 whitespace * Add basic CUDA error throwing and templateize set * Explicit contiguous and AT_DISPATCH_ALL_TYPES return * Remove extraneous numpy conversion * Refactor out .data calls * Refactored to variable return length API with wrapper fn as opposed to returning a 0-length tensor, per off-line reviewer comments * Remove A * Don't use hidden torch._unique() in test * Fix documentations
Questions/possible future works:
Testing command:
DEBUG=1 NO_CUDA=1 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py build && DEBUG=1 NO_CUDA=1 MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py develop && python3 test/test_torch.py
Commands to preview generated documentations:
cd docs
pip install -r requirements.txt
make html