TensorFlow中的矩阵行列式求导

8

我希望使用TensorFlow计算矩阵行列式的导数。从实验中可以看出,TensorFlow没有实现通过行列式求导的方法:

LookupError: No gradient defined for operation 'MatrixDeterminant' 
(op type: MatrixDeterminant)

进一步的调查发现,实际上可以计算导数;例如,请参见Jacobi公式。我确定为了实现通过行列式进行求导的方法,我需要使用函数装饰器。
@tf.RegisterGradient("MatrixDeterminant")
def _sub_grad(op, grad):
    ...

然而,我对TensorFlow并不熟悉,无法理解如何实现。有没有人能提供一些见解吗?
以下是一个我遇到这个问题的例子:
x = tf.Variable(tf.ones(shape=[1]))
y = tf.Variable(tf.ones(shape=[1]))

A = tf.reshape(
    tf.pack([tf.sin(x), tf.zeros([1, ]), tf.zeros([1, ]), tf.cos(y)]), (2,2)
)
loss = tf.square(tf.matrix_determinant(A))


optimizer = tf.train.GradientDescentOptimizer(0.001)
train = optimizer.minimize(loss)

init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)


for step in xrange(100):
    sess.run(train)
    print sess.run(x)
3个回答

9
请查看“在Python中实现梯度”部分,链接在这里
具体来说,您可以按以下方式实现。
@ops.RegisterGradient("MatrixDeterminant")
def _MatrixDeterminantGrad(op, grad):
  """Gradient for MatrixDeterminant. Use formula from 2.2.4 from
  An extended collection of matrix derivative results for forward and reverse
  mode algorithmic differentiation by Mike Giles
  -- http://eprints.maths.ox.ac.uk/1079/1/NA-08-01.pdf
"""
  A = op.inputs[0]
  C = op.outputs[0]
  Ainv = tf.matrix_inverse(A)
  return grad*C*tf.transpose(Ainv)

接下来是一个简单的训练循环,用于检查它是否有效:

a0 = np.array([[1,2],[3,4]]).astype(np.float32)
a = tf.Variable(a0)
b = tf.square(tf.matrix_determinant(a))
init_op = tf.initialize_all_variables()
sess = tf.InteractiveSession()
init_op.run()

minimization_steps = 50
learning_rate = 0.001
optimizer = tf.train.GradientDescentOptimizer(learning_rate)
train_op = optimizer.minimize(b)

losses = []
for i in range(minimization_steps):
  train_op.run()
  losses.append(b.eval())

那么您可以随时查看时间轴上的损失情况。
import matplotlib.pyplot as plt

plt.ylabel("Determinant Squared")
plt.xlabel("Iterations")
plt.plot(losses)

应该看到类似于这样的东西 损失图

非常酷!由于某些原因,tf文档引起了问题。例如:从上面的链接中http://tensorflow.org/how_tos/adding_an_op/index.md#AUTOGENERATED-implement-the-gradient-in-python - Blaze

0

我认为你对于矩阵行列式的导数有所困惑。

矩阵行列式是通过某种公式计算矩阵元素而得到的函数。因此,如果矩阵的所有元素都是数字,则行列式将会是一个数字,其导数将会是0。当一些元素是变量时,你会得到这些变量的表达式。例如:

x, x^2
1, sin(x)

行列式为 x*sin(x) - x^2,导数为 2x + sin(x) + x*cos(x)。Jacobi公式将行列式与伴随矩阵连接起来。


在你的例子中,矩阵A仅由数字组成,因此行列式只是一个数字,而loss也只是一个数字。GradientDescentOptimizer需要一些自由变量来进行最小化,但由于你的loss只是一个数字,所以没有自由变量可用。

这里的真正问题是MatrixDeterminant类没有提供已注册的梯度。 - user1936768
@user1936768,是的,这就是你在Python问题中出错的原因之一,但这并不是真正的原因。假设梯度方法存在,它将始终返回0,无论如何。这对你的100次迭代有任何帮助吗?它到底如何最小化任何东西? - Salvador Dali
不,梯度不会为零。我正在对x和y进行最小化处理,而矩阵通过正弦和余弦函数分别依赖于x和y。 - user1936768
无论如何,这都不是重点。我的例子只是一个例子。很容易想象出(更多)矩阵依赖于变量的情况。在这些情况下,MatrixDeterminant没有梯度这一事实会带来问题。 - user1936768
@Salvador,你误解了这个问题。有一种方法可以从输入矩阵中的数字计算行列式(一个数字)。我们正在谈论关于输入矩阵每个元素的行列式操作的导数,在当前输入值处进行评估。当x=5时,x**2的导数是多少? - mdaoust
显示剩余2条评论

0

对于那些感兴趣的人,我发现了解决我的问题的方法:

@tf.RegisterGradient("MatrixDeterminant")
def _MatrixDeterminant(op, grad):
    """Gradient for MatrixDeterminant."""
    return op.outputs[0] * tf.transpose(tf.matrix_inverse(op.inputs[0]))

1
如果你在行列式上方有其他内容(例如,如果你最小化行列式的平方),那么它将无法正确地进行反向传播。 - Yaroslav Bulatov

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