Optimization 矢量化索引识别,加快GPU处理速度

Optimization 矢量化索引识别,加快GPU处理速度,optimization,pytorch,gpu,Optimization,Pytorch,Gpu,我的输入是一个值列表,data\u idx。在本例中,值的范围为[0,5] data_idx = [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0] 我想要的输出,filled\u matrix是一个形状为max(value)bylen(data\u idx)的张量,其中张量的每一行,r包含所有索引,其中data\u idx==r如果匹配的索引数小于len(data\u idx) 例如,在第一行中,r=0,data\u idx==0位于索引[3,11,1

我的输入是一个值列表,
data\u idx
。在本例中,值的范围为[0,5]

data_idx = [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0]
我想要的输出,
filled\u matrix
是一个形状为
max(value)
by
len(data\u idx)
的张量,其中张量的每一行,
r
包含所有索引,其中
data\u idx==r
如果匹配的索引数小于
len(data\u idx)

例如,在第一行中,
r=0
data\u idx==0
位于索引
[3,11,14]
。完整输出如下所示:

filled_matrix = tensor([[ 3, 11, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1], 
    [ 5, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [ 0,  9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [ 8, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [ 4,  6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [ 1,  2,  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]],
   dtype=torch.int8)
我一直在为实现我的目标的循环代码工作

import torch

max_idx = 6
data_idx = torch.tensor([2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0]).cuda()

max_number_data_idx = data_idx.shape[0]

filled_matrix = torch.zeros([max_idx, max_number_data_idx], dtype=torch.int8, device='cuda')
filled_matrix.fill_(-1)
for i in range(max_idx):
   same_idx = (data_idx == i).nonzero().flatten()
   filled_matrix[i][:same_idx.shape[0]] = same_idx

现在,我想加速这个代码。具体来说,我希望它在GPU上更快。在实际场景中,输入,
data\u idx
,可以是一个包含数百万个值的列表。在这种情况下,例如1 M不同的值,GPU将被调用1 M的时间,这使得它非常慢。我的代码是顺序代码,GPU讨厌顺序代码

是否有一个函数可以更有效地产生相同的结果?或者是一种将这个for循环矢量化的方法?

免责声明:我还没有分析过这段代码,看看它在GPU上是否真的更快。

一种矢量化解决方案是使用张量视图来广播比较。张量视图不使用额外的内存。您可以在中看到更多详细信息

首先,制作一个矩阵,其中包含要对每行进行比较的值。在这种情况下,它只是行索引

comparison = torch.tensor(range(max_idx))
现在,我们将使用并查看
数据\u idx
比较
,它们的形状与
填充矩阵
相同

comparison_view = comparison.unsqueeze(1).expand(max_idx, max_number_data_idx)
print(comparison_view)

# Each row is the index you want to compare to
# tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
    [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
    [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
    [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]])

data_idx_view = data_idx.expand(max_idx, max_number_data_idx)
print(data_idx_view)

# Each row is a copy of data_idx
# tensor([[2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0]])
我们可以比较它们的相等性并使用它们来找到索引

mask = comparison_view == data_idx_view
mask_indices = mask.nonzero()

print(mask_indices)
# tensor([[ 0,  3],
    [ 0, 11],
    [ 0, 14],
    [ 1,  5],
    [ 1, 10],
    [ 2,  0],
    [ 2,  9],
    [ 3,  8],
    [ 3, 12],
    [ 3, 13],
    [ 4,  4],
    [ 4,  6],
    [ 5,  1],
    [ 5,  2],
    [ 5,  7]])
现在,您只需要将这些结果转换为您想要的输出格式

filled_matrix = torch.zeros([max_idx, max_number_data_idx], dtype=torch.int8)
filled_matrix.fill_(-1)
col_indices = [0, 1, 2, 0, 1, 0, 1, 0, 1, 2, 0, 1, 0, 1, 2]
filled_matrix[mask_indices[:, 0], col_indices] = mask_indices[:, 1].type(torch.int8)
我考虑了几个选项来生成
col\u索引
列表,但是如果没有for循环,我就什么都想不出来

col_indices = torch.zeros(mask_indices.shape[0])
for i in range(1, mask_indices.shape[0]):
    if mask_indices[i,0] == mask_indices[i-1,0]:
        col_indices[i] = col_indices[i-1]+1
您需要进行一些评测,以查看哪些代码实际上更快。

免责声明:我还没有对这些代码进行评测,以查看它在GPU上是否确实更快。

一种矢量化解决方案是使用张量视图来广播比较。张量视图不使用额外的内存。您可以在中看到更多详细信息

首先,制作一个矩阵,其中包含要对每行进行比较的值。在这种情况下,它只是行索引

comparison = torch.tensor(range(max_idx))
现在,我们将使用并查看
数据\u idx
比较
,它们的形状与
填充矩阵
相同

comparison_view = comparison.unsqueeze(1).expand(max_idx, max_number_data_idx)
print(comparison_view)

# Each row is the index you want to compare to
# tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
    [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
    [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
    [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]])

data_idx_view = data_idx.expand(max_idx, max_number_data_idx)
print(data_idx_view)

# Each row is a copy of data_idx
# tensor([[2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0],
    [2, 5, 5, 0, 4, 1, 4, 5, 3, 2, 1, 0, 3, 3, 0]])
我们可以比较它们的相等性并使用它们来找到索引

mask = comparison_view == data_idx_view
mask_indices = mask.nonzero()

print(mask_indices)
# tensor([[ 0,  3],
    [ 0, 11],
    [ 0, 14],
    [ 1,  5],
    [ 1, 10],
    [ 2,  0],
    [ 2,  9],
    [ 3,  8],
    [ 3, 12],
    [ 3, 13],
    [ 4,  4],
    [ 4,  6],
    [ 5,  1],
    [ 5,  2],
    [ 5,  7]])
现在,您只需要将这些结果转换为您想要的输出格式

filled_matrix = torch.zeros([max_idx, max_number_data_idx], dtype=torch.int8)
filled_matrix.fill_(-1)
col_indices = [0, 1, 2, 0, 1, 0, 1, 0, 1, 2, 0, 1, 0, 1, 2]
filled_matrix[mask_indices[:, 0], col_indices] = mask_indices[:, 1].type(torch.int8)
我考虑了几个选项来生成
col\u索引
列表,但是如果没有for循环,我就什么都想不出来

col_indices = torch.zeros(mask_indices.shape[0])
for i in range(1, mask_indices.shape[0]):
    if mask_indices[i,0] == mask_indices[i-1,0]:
        col_indices[i] = col_indices[i-1]+1

您需要进行一些评测,以查看哪些代码实际上更快。

您能再解释一下您的目标是什么,哪些不起作用吗?例如,您想要的输出是什么?你得到了什么?谢谢你的回答,我的输出是“填充矩阵”。我想找到一种方法在GPU上优化这段代码,并获得相同的输出。所以我想澄清一下,这段代码在GPU上做了你想要的事情,现在你想把它移到cpu上?它在这两个方面都起作用。我想优化它只为gpu。下面的代码只是一个例子。在实际场景中,输入data_idx可以是一个包含数百万个值的列表。在这种情况下,例如1 M不同的值,gpu将被调用1 M的时间,这使得它非常慢。我的代码是顺序代码,gpu讨厌顺序代码。因此,对我来说,第一种选择是要么设法避免使用循环(可能有一个隐藏的pytorch函数,我不知道),要么设法并行化循环。再次感谢你基于你的评论,我试着把你的问题写得更清楚一点。希望这能帮助你找到你想要的答案。你能再解释一下你的目标是什么,什么不起作用吗?例如,您想要的输出是什么?你得到了什么?谢谢你的回答,我的输出是“填充矩阵”。我想找到一种方法在GPU上优化这段代码,并获得相同的输出。所以我想澄清一下,这段代码在GPU上做了你想要的事情,现在你想把它移到cpu上?它在这两个方面都起作用。我想优化它只为gpu。下面的代码只是一个例子。在实际场景中,输入data_idx可以是一个包含数百万个值的列表。在这种情况下,例如1 M不同的值,gpu将被调用1 M的时间,这使得它非常慢。我的代码是顺序代码,gpu讨厌顺序代码。因此,对我来说,第一种选择是要么设法避免使用循环(可能有一个隐藏的pytorch函数,我不知道),要么设法并行化循环。再次感谢你基于你的评论,我试着把你的问题写得更清楚一点。希望这能帮助你得到你想要的答案。很抱歉回答太晚了。非常感谢您的帮助,它工作得很好。我在我的真实代码上试用过,速度快了3-4倍。为了生成col_索引,我使用了其他方法,如下所述:。再次感谢你,这真的帮了我很大的忙!对不起,我迟了答复。非常感谢您的帮助,它工作得很好。我在我的真实代码上试用过,速度快了3-4倍。为了生成col_索引,我使用了其他方法,如下所述:。再次感谢你,这真的帮了我很大的忙!