我想实现一个类似于torch.unique
的inverse
值的“反向”Map的函数。
例如,torch.unique
函数可以返回长类型输入x
的唯一值uniques
和从uniques
Map到x
的逆Tensor。
x = torch.LongTensor([9, 10, 9, 9, 10, 9])
uniques, inverse, counts = torch.unique(x, return_inverse=True, return_counts=True)
# uniques = [9, 10]
# inverse = [0, 1, 0, 0, 1, 0]
# counts = [4, 2]
print((uniques[inverse] == x).all()) # True
字符串
对于我的问题,是否有一些有效的方法来获得从x
Map到uniques
的“反向”逆back_map
?
def reverse_unique(x): ...
uniques, inverse, counts, back_map = reverse_unique(x)
# uniques = [9, 10]
# inverse = [0, 1, 0, 0, 1, 0]
# counts = [4, 2]
# back_map = [0, 2, 3, 5, 1, 4]
print((x[back_map] == uniques.repeat_interleave(counts)).all()) # True
型
在上面的代码中,back_map
将inverse
的值Map到输入x
的位置。
我知道用python循环实现这个函数并不困难,但在我的例子中,输入的x
大小可以达到e8,所以时间开销是无法忍受的。
那么,有没有使用pytorch API或cuda内核的高级实现(我试图使用cuda扩展来并行化它,但我的cuad内核非常慢:sob:)?
__global__ void unique_back_map_kernel(
int32_t num_uni,
int32_t num_x,
int64_t* __restrict__ uniques,
int64_t* __restrict__ cumsum_counts,
int64_t* __restrict__ x,
int64_t* __restrict__ out) {
int32_t n = blockIdx.x * blockDim.x + threadIdx.x;
if (n >= num_uni) {
return;
}
size_t counts = 0;
auto idx = __ldg(&cumsum_counts[n]);
#pragma unroll
for (int64_t i = 0; i < num_x; ++i) {
if (x[i] == uniques[n]) {
out[idx + counts] = i;
counts++;
}
}
}
std::tuple<at::Tensor, at::Tensor, at::Tensor, at::Tensor> reverse_unique(at::Tensor x) {
TORCH_CHECK(x.dtype() == at::kLong && x.device().is_cuda());
auto [uniques, inverse, counts] = at::_unique2(x, false, true, true);
counts.cumsum_(0);
// python: cumsum_counts = torch.cat([torch.tensor([0]), cumsum_counts[:-1]])
auto cumsum_counts = at::cat({at::zeros({1}, counts.options()), counts.slice(0, 0, -1)});
auto back_map = at::empty_like(x);
int32_t threads = (uniques.numel() > 256) ? 256 : 32;
int32_t blocks = (uniques.numel() + threads - 1) / threads;
unique_back_map_kernel<<<blocks, threads, 0, c10::cuda::getCurrentCUDAStream()>>>(
uniques.numel(),
x.numel(),
uniques.data_ptr<int64_t>(),
cumsum_counts.data_ptr<int64_t>(),
x.data_ptr<int64_t>(),
back_map.data_ptr<int64_t>());
return std::make_tuple(uniques, inverse, cumsum_counts, back_map);
}
型
2条答案
按热度按时间mqkwyuun1#
@alexey-birukov可能是指:
如果你阅读
torch.unique
的文档,你会发现它基本上只是对值进行排序,然后执行torch.unique_consecutive
:当前在CUDA实现和CPU实现中,当指定dim时,torch.unique 总是在开始时对Tensor进行排序,而不管 sort 参数如何。排序可能会很慢,所以如果输入Tensor已经排序,建议使用
torch.unique_consecutive()
,以避免排序。再看看
torch.sort
的文档,你会知道它不仅会返回排序后的值,还会返回索引的排列以恢复原始顺序。这个排列似乎正是你想要的。字符串
gjmwrych2#
字符串