让我们看一个包含单列的示例,例如:[[x1], [x2], [x3]]
.
Let sum
be x1 + x2 + x3
,然后标准化x
会给y = [[y1], [y2], [y3]] = [[x1/sum], [x2/sum], [x3/sum]]
。您正在寻找dL/dx1
, dL/x2
, and dL/x3
- 我们将它们写为:dx1
, dx2
, and dx3
。对所有人都一样dL/dyi
.
So dx1
等于dL/dy1*dy1/dx1 + dL/dy2*dy2/dx1 + dL/dy3*dy3/dx1
。那是因为x1
贡献于相应列上的所有输出元素:y1
, y2
, and y3
.
We have:
-
dy1/dx1 = d(x1/sum)/dx1 = (sum - x1)/sum²
-
dy2/dx1 = d(x2/sum)/dx1 = -x2/sum²
-
相似地,dy3/dx1 = d(x3/sum)/dx1 = -x3/sum²
所以dx1 = (sum - x1)/sum²*dy1 - x2/sum²*dy2 - x3/sum²*dy3
。同样适用于dx2
and dx3
。因此,雅可比行列式是[dxi]_i = (sum - xi)/sum²
and [dxi]_j = -xj/sum²
(对全部j
不同于i
).
在您的实现中,您似乎缺少所有非对角线组件。
保持相同的一列示例,x1=2
, x2=3
, and x3=5
:
>>> x = torch.tensor([[2.], [3.], [5.]])
>>> sum = input.sum(0)
tensor([10])
雅可比行列式将是:
>>> J = (sum*torch.eye(input.size(0)) - input)/sum**2
tensor([[ 0.0800, -0.0200, -0.0200],
[-0.0300, 0.0700, -0.0300],
[-0.0500, -0.0500, 0.0500]])
对于具有多列的实现,这有点棘手,更具体地说对于对角矩阵的形状。更容易保留column轴放在最后,这样我们就不必为广播而烦恼:
>>> x = torch.tensor([[2., 1], [3., 3], [5., 5]])
>>> sum = x.sum(0)
tensor([10., 9.])
>>> diag = sum*torch.eye(3).unsqueeze(-1).repeat(1, 1, len(sum))
tensor([[[10., 9.],
[ 0., 0.],
[ 0., 0.]],
[[ 0., 0.],
[10., 9.],
[ 0., 0.]],
[[ 0., 0.],
[ 0., 0.],
[10., 9.]]])
Above diag
形状为(3, 3, 2)
哪里两个columns位于最后一个轴上。请注意我们如何不需要广播sum
.
What I wouldn't所做的是:torch.eye(3).unsqueeze(0).repeat(len(sum), 1, 1)
。既然是这样的形状——(2, 3, 3)
- 你将不得不使用sum[:, None, None]
,并且需要进一步广播......
雅可比行列式很简单:
>>> J = (diag - x)/sum**2
tensor([[[ 0.0800, 0.0988],
[-0.0300, -0.0370],
[-0.0500, -0.0617]],
[[-0.0200, -0.0123],
[ 0.0700, 0.0741],
[-0.0500, -0.0617]],
[[-0.0200, -0.0123],
[-0.0300, -0.0370],
[ 0.0500, 0.0494]]])
您可以通过使用任意的操作反向传播来检查结果dy
矢量(不与torch.ones
不过,你会得到0
是因为J
!)。经过反向传播后,x.grad
应等于torch.einsum('abc,bc->ac', J, dy)
.