以下代码计算实对称矩阵的特征值分解。然后,计算第一特征值相对于矩阵的梯度。这一共进行了三次:1(使用解析公式,2(使用TensorFlow,3(使用PyTorch。这产生了三种不同的结果。有人能向我解释一下这种行为吗?
import numpy as np
import torch
import tensorflow as tf
np.set_printoptions(precision=3)
np.random.seed(123)
# random matrix
matrix_np = np.random.randn(4, 4)
# make symmetric
matrix_np = matrix_np + matrix_np.T
matrix_torch = torch.autograd.Variable(torch.from_numpy(matrix_np), requires_grad=True)
matrix_tf = tf.constant(matrix_np, dtype=tf.float64)
#
# compute eigenvalue decompositions
#
# NumPy
eigvals_np, eigvecs_np = np.linalg.eigh(matrix_np)
# PyTorch
eigvals_torch, eigvecs_torch = torch.symeig(matrix_torch, eigenvectors=True, upper=True)
# TensorFlow
eigvals_tf, eigvecs_tf = tf.linalg.eigh(matrix_tf)
# make sure all three versions computed the same eigenvalues
if not np.allclose(eigvals_np, eigvals_torch.data.numpy()):
print('NumPy and PyTorch have different eigenvalues')
if not np.allclose(eigvals_np, tf.keras.backend.eval(eigvals_tf)):
print('NumPy and TensorFlow have different eigenvalues')
#
# compute derivative of first eigenvalue with respect to the matrix
#
# analytic gradient, see "On differentiating eigenvalues and eigenvectors" by Jan R. Magnus
grad_analytic = np.outer(eigvecs_np[:, 0], eigvecs_np[:, 0])
# PyTorch gradient
eigvals_torch[0].backward()
grad_torch = matrix_torch.grad.numpy()
# TensorFlow gradient
grad_tf = tf.gradients(eigvals_tf[0], matrix_tf)[0]
grad_tf = tf.keras.backend.eval(grad_tf)
#
# print all derivatives
#
print('-'*6, 'analytic gradient', '-'*6)
print(grad_analytic)
print('-'*6, 'Pytorch gradient', '-'*6)
print(grad_torch)
print('-'*6, 'TensorFlow gradient', '-'*6)
print(grad_tf)
打印
------ analytic gradient ------
[[ 0.312 -0.204 -0.398 -0.12 ]
[-0.204 0.133 0.26 0.079]
[-0.398 0.26 0.509 0.154]
[-0.12 0.079 0.154 0.046]]
------ Pytorch gradient ------
[[ 0.312 -0.407 -0.797 -0.241]
[ 0. 0.133 0.52 0.157]
[ 0. 0. 0.509 0.308]
[ 0. 0. 0. 0.046]]
------ TensorFlow gradient ------
[[ 0.312 0. 0. 0. ]
[-0.407 0.133 0. 0. ]
[-0.797 0.52 0.509 0. ]
[-0.241 0.157 0.308 0.046]]
三个结果的主对角线是相同的。TensorFlow和PyTorch的非对角元素是解析元素的两倍大或等于零。
这是故意的行为吗?为什么没有记录?坡度错了吗?
版本信息:TensorFlow 1.14.0,PyTorch 1.0.1
相对于保证对称的矩阵的梯度并不是真正定义明确的(在对角线外(,因为有效的实现可能取决于元素或其相反元素(或两者的加权和(。
例如,对2x2对称矩阵x
的元素求和的函数的有效实现将是
f(x) = x[0][0]+x[0][1]+x[1][0]+x[1][1]
但另一种有效的实现方式是
f(x) = x[0][0]+x[1][1]+2*x[0][1]
如果对称矩阵是保证矩阵始终对称的较大计算的一部分(例如x = [[a, b], [b, c]]
,其中a
、b
和c
是一些标量(,那么更大计算的梯度不受如何定义对称矩阵函数的梯度的影响(在我运行的例子中,无论如何定义f
,我们都有df/da = df/dc = 1
和df/db = 2
(。
也就是说,对称梯度是一个不错的选择(正如评论中链接的PyTorch PR中所解释的(,因为这意味着如果你碰巧在对称矩阵上进行梯度下降更新,矩阵将保持对称。
此外,请注意,TensorFlow确实记录了只有矩阵的下三角部分用于计算,并有意相应地调整报告的梯度。