PyTorch中如何用向量替换对角线元素

9

我已经到处寻找与PyTorch等效的东西,但是我找不到任何东西。

L_1 = np.tril(np.random.normal(scale=1., size=(D, D)), k=0)
L_1[np.diag_indices_from(L_1)] = np.exp(np.diagonal(L_1))

我猜想使用Pytorch没有一种优雅的方式可以替换对角线元素。
4个回答

4

有一种更简单的方法来完成它

dest_matrix[range(len(dest_matrix)), range(len(dest_matrix))] = source_vector

实际上我们需要自己生成对角线索引。 使用示例:
dest_matrix = torch.randint(10, (3, 3))
source_vector = torch.randint(100, 200, (len(dest_matrix), ))
print('dest_matrix:\n', dest_matrix)
print('source_vector:\n', source_vector)

dest_matrix[range(len(dest_matrix)), range(len(dest_matrix))] = source_vector

print('result:\n', dest_matrix)

# dest_matrix:
#  tensor([[3, 2, 5],
#         [0, 3, 5],
#         [3, 1, 1]])
# source_vector:
#  tensor([182, 169, 147])
# result:
#  tensor([[182,   2,   5],
#         [  0, 169,   5],
#         [  3,   1, 147]])

如果dest_matrix不是正方形,则在range()中应使用min(dest_matrix.size())而非len(dest_matrix)

这种方法不如numpy优雅,但它不需要存储新的索引矩阵。

是的,这保留了梯度


3

我认为目前还没有实现这样的功能。但是,您可以使用mask来实现相同的功能,方法如下。

# Assuming v to be the vector and a be the tensor whose diagonal is to be replaced
mask = torch.diag(torch.ones_like(v))
out = mask*torch.diag(v) + (1. - mask)*a

所以,你的实现应该类似于:
L_1 = torch.tril(torch.randn((D, D)))
v = torch.exp(torch.diag(L_1))
mask = torch.diag(torch.ones_like(v))
L_1 = mask*torch.diag(v) + (1. - mask)*L_1

并不像numpy那样优雅,但也不太糟糕。

实际上,我期望的是更加优雅的解决方案,但这个也可以。 - azal
如果张量和向量都需要 grad,那么这个解决方案是否会保留 grad 要求? - azal
是的。我想是这样的,因为在PyTorch中使用的所有函数都是可微分的。你可以使用它并查看是否出现任何错误。 - layog
你能解释一下这段代码吗? - OuttaSpaceTime

2

您可以使用 diagonal() 提取对角线元素,然后使用 copy_() 将转换后的值就地赋值:

new_diags = L_1.diagonal().exp()
L_1.diagonal().copy_(new_diags)

更简单的常数情况:https://dev59.com/tqrka4cB1Zd3GeqPZCRx#66760701 - iacob

1

为了简单起见,假设您有一个矩阵L_1并希望用零替换其对角线。 您可以使用多种方法来实现此目的。

使用fill_diagonal_()

L_1 = torch.tensor([[1,2,3],[4,5,6],[7,8,9]], dtype=torch.float32)
L_1 = L_1.fill_diagonal_(0.)

使用高级索引:
L_1 = torch.tensor([[1,2,3],[4,5,6],[7,8,9]], dtype=torch.float32)
length = len(L_1)
zero_vector = torch.zeros(length, dtype=torch.float32)
L_1[range(length), range(length)] = zero_vector

使用scatter_()函数:
L_1 = torch.tensor([[1,2,3],[4,5,6],[7,8,9]], dtype=torch.float32)  
diag_idx = torch.arange(len(L_1)).unsqueeze(1)
zero_matrix = torch.zeros(L_1.shape, dtype=torch.float32)
L_1 = L_1.scatter_(1, diag_idx, zero_matrix) 

请注意,上述所有解决方案都是原地操作,并且会影响反向传递,因为可能需要计算原始值。因此,如果您想保持反向传递不受影响,即通过不记录更改(操作)来“断开图形”,即在反向传递中不计算与前向传递中计算的梯度相对应的梯度,则可以在使用高级索引或scatter_()时添加.data。
使用.data进行高级索引:
L_1 = torch.tensor([[1,2,3],[4,5,6],[7,8,9]], dtype=torch.float32)
length = len(L_1)
zero_vector = torch.zeros(length, dtype=torch.float32)
L_1[range(length), range(length)] = zero_vector.data

使用scatter_().data

L_1 = torch.tensor([[1,2,3],[4,5,6],[7,8,9]], dtype=torch.float32)  
diag_idx = torch.arange(len(L_1)).unsqueeze(1)
zero_matrix = torch.zeros(L_1.shape, dtype=torch.float32)
L_1 = L_1.scatter_(1, diag_idx, zero_matrix.data)

请参考this讨论。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接