假设我想计算关于某些参数W(例如前馈神经网络的权重和偏差)的标量值函数的Hessian . 如果您考虑以下代码,则实施经过培训以最小化MSE损失的二维线性模型:
import numpy as np
import tensorflow as tf
x = tf.placeholder(dtype=tf.float32, shape=[None, 2]) #inputs
t = tf.placeholder(dtype=tf.float32, shape=[None,]) #labels
W = tf.placeholder(np.eye(2), dtype=tf.float32) #weights
preds = tf.matmul(x, W) #linear model
loss = tf.reduce_mean(tf.square(preds-t), axis=0) #mse loss
params = tf.trainable_variables()
hessian = tf.hessians(loss, params)
你希望 session.run(tf.hessian,feed_dict={})
返回一个2x2矩阵(等于W) . 事实证明,因为 params
是一个2x2张量,输出是一个形状为[2,2,2,2]的张量 . 虽然我可以很容易地重新形成张量以获得我想要的矩阵,但是当 params
成为不同大小的张量列表时(例如,当模型是深度神经网络时),这个操作似乎非常麻烦 .
看来这有两种方式:
- Flatten
params
是一个名为flat_params
的1D张量:
flat_params = tf.concat([tf.reshape(p, [-1]) for p in params])
这样 tf.hessians(loss, flat_params)
自然地返回一个2x2矩阵 . 但是如Why does Tensorflow Reshape tf.reshape() break the flow of gradients?中针对tf.gradients所述(但也适用于tf.hessians),tensorflow无法在 params
和 flat_params
之间看到图形中的符号链接,并且 tf.hessians(loss, flat_params)
将引发错误,因为渐变将被视为 None
.
- 在https://afqueiruga.github.io/tensorflow/2017/12/28/hessian-mnist.html中,代码的作者采用另一种方式,首先创建flat参数并将其部件重新整形为
self.params
. 这个技巧确实有效,可以获得具有预期形状(2x2矩阵)的粗麻布 . 但是,在我看来,当你有一个复杂的模型时,这将很麻烦,如果你通过内置函数(如tf.layers.dense
,..)创建模型,则无法应用 .
当 self.params
是任意形状的张量列表时,是否没有直接的方法从 tf.hessians
获得Hessian矩阵(如本例中的2x2矩阵)?如果没有,你怎么能自动化 tf.hessians
的输出张量的整形呢?
1 回答
事实证明(至少每TensorFlow v1.12)tf.hessians只返回Hessian矩阵的对角元素 . 完整的Hessian是PxP矩阵,其中P是模型中参数的数量(参见https://en.wikipedia.org/wiki/Hessian_matrix) . 换句话说,在你的情况下,你应该期望一个4x4矩阵,但你得到的只是四个(对角线)元素 . 要获得完整的Hessian矩阵,您可以创建自己的函数,如本书所示(第31页):https://books.google.no/books?id=qkFPDwAAQBAJ&pg=PA31&lpg=PA31&dq=tensorflow+hessian&source=bl&ots=U-GeIoIITz&sig=rxoHuO7HpLxRogh24gWnJAx_uHE&hl=no&sa=X&ved=2ahUKEwiJk_bSx-PeAhWEpIsKHW2gC1c4ChDoATAAegQICRAB#v=onepage&q&f=false
不幸的是,随着参数数量的增加,这种双重tf.gradients方法变得非常低效 - 但是首先将完整的Hessian存储为大型模型是有问题的 .
这可能不是你想要的答案,但希望它有所帮助!