Uh oh!
There was an error while loading. Please reload this page.
[Frontend][Tensorflow] Add unique operator - #7441
Conversation
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Thanks, I was planning to work on unique next week, happy to collaborate. I can add TIR unqiue impl both cpu and gpu later. We can add relay boilarplate, temp impl in cpp, and tests in this PR. |
ymwangg
commented
Feb 11, 2021
That would be great! |
@ymwangg For a general op like Numpy and PyTorch supports We can implement |
ymwangg
commented
Feb 17, 2021
@masahi Thanks for your comment. # topidefunique(data, data_sorted, data_argsorted):
output= [0] *len(data)
count= [0] *len(data)
first_occurrence= [len(data)] *len(data)
inverse_indices= [0] *len(data)
num_unique=0# ir_builderforiinrange(len(data)):
ifi==0ordata_sorted[i] !=data_sorted[i-1]:
num_unique+=1output[num_unique-1] =data_sorted[i]
first_occurrence[num_unique-1] =min(first_occurrence[num_unique-1], data_argsorted[i])
count[num_unique-1] +=1inverse_indices[data_argsorted[i]] =num_unique-1returnoutput, count, first_occurrence, inverse_indices, num_unique# tf front enddeftf_unique(data):
output, count, first_occurrence, inverse_indices, num_unique=unique(data, np.sort(data), np.argsort(data))
sorted_occurence_indices=np.argsort(first_occurrence) # relay.argsortnew_output= [output[sorted_occurence_indices[i]] foriinrange(num_unique)] # relay.takeindex_converter=np.argsort(sorted_occurence_indices) # relay.argsortnew_inverse_indices= [index_converter[i] foriininverse_indices] # relay.takereturnnew_output, new_inverse_indicesIt defines a topi function that is similar to Does this look good to you? |
It can be a lot simpler than that. Unique is basically sort + adjacent difference + exclusive scan. If you don't understand that statement, the following example should help. We have exclusive scan for CPU ( If we implement unique this way, the same code runs on both CPU and GPU. Output: |
codeislife99
commented
Feb 17, 2021
Hey @masahi , can your example be extended to provide |
masahi
commented
Feb 17, 2021
Yes, it's possible but a bit complicated. PyTorch also has I think for the first PR, not all options need to be implemented. We can follow up later. I'm using PyTorch GPU impl as reference, see for example below on how they support count |
codeislife99
commented
Feb 17, 2021
I see, I was interested in |
ymwangg
commented
Feb 17, 2021
@masahi Thanks for the explanation and it is very helpful! sorted_data=relay.sort(data)
argsort_indices=relay.argsort(data)
adj_diff=relay.adjacent_difference(sorted_data, first_value=0, "not_equal")
ex_scan=relay.cumsum(adj_diff, exclusive=True)
inverse_indices=relay.scatter(data, argsort_indices, ex_scan)
unique=relay.scatter(data, ex_scan, sorted_data)
unique_sliced=relay.strided_slice(unique, [0], relay.take(ex_scan,[-1]), slice_mode="size")
returnunique_sliced, inverse_indicesI saw PyTorch uses To support counting, it looks like we need to implement a |
For your first implementation, combination-based approach is ok. But So use ir builder if you are comfortable with it, otherwise combination of relay ops is fine. Performance + support for options can be done later (by me). Don't worry about |
Add unit tests for unique operator
masahi
commented
Feb 20, 2021
Looks good 👍 GPU is not supported right? |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
masahi
commented
Feb 20, 2021
Can you also add pytorch frontend? Not all option need to be supported. Likely the same as tf conversion |
@masahi Yeah, I only added CPU version in this PR. I'm not very familiar with GPU IR now but I can do it later. If the overall structure looks good, I can add I'll add the pytorch frontend in this PR. |
masahi
commented
Feb 20, 2021
I can do the GPU version. It will likely require ir builder. But let me know if you want to do GPU as well, you can certainly do it. The idea is identical with CPU version, just using different parallelization. If |
ymwangg
commented
Feb 20, 2021
@masahi I added the I'll work on the GPU version of |
Uh oh!
There was an error while loading. Please reload this page.
ymwangg
commented
Feb 23, 2021
@masahi I added the GPU version and it's ready for review. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
@ymwangg@codeislife99 I found a neat trick PyTorch uses for Basically, after you get ex scan, instead of copying from the original input, you copy from an array [0, 1, 2, ....]. This will give you something like [0, 2, 5], and doing adjacent element on it directly gives the count. Does this make sense? It should be much faster than atomic. |
ymwangg
commented
Feb 24, 2021
@masahi thanks. I'll try using |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
masahi
commented
Feb 26, 2021
Thanks @ymwangg@codeislife99, this is really a great work! |
ymwangg
commented
Feb 26, 2021
@masahi thanks for making this such an interesting project! |
* Initial commit of the unique operator Add unit tests for unique operator * Add tensorflow unique op * Refactor unique to use sort-based algorithm * Change relay.unique test to run only on cpu * Change topi.unique test to run only on cpu * Change range to parallel for parallelizable loops * Add return_counts option for relay.unique and topi.unique, add pytorch frontend * Fix pylint * Patch pytorch frontend * Initial support of topi.cuda.unique * Refactor to use ir_builder directly * Modularize adjacent difference * Refactor to simplify * Fix typo * Combine _unique and _unique_with_counts * Reuse indices_ptr to remove arange_ptr Co-authored-by: Yanming Wang <yanmwang@amazon.com>
* Initial commit of the unique operator Add unit tests for unique operator * Add tensorflow unique op * Refactor unique to use sort-based algorithm * Change relay.unique test to run only on cpu * Change topi.unique test to run only on cpu * Change range to parallel for parallelizable loops * Add return_counts option for relay.unique and topi.unique, add pytorch frontend * Fix pylint * Patch pytorch frontend * Initial support of topi.cuda.unique * Refactor to use ir_builder directly * Modularize adjacent difference * Refactor to simplify * Fix typo * Combine _unique and _unique_with_counts * Reuse indices_ptr to remove arange_ptr Co-authored-by: Yanming Wang <yanmwang@amazon.com>
This PR adds the tensorflow
uniqueoperator as described in https://www.tensorflow.org/api_docs/python/tf/unique.I'm not sure I follow the best practices. Comments and suggestions are welcome. @yongwww@kevinthesun@codeislife99