python - 在 TensorFlow2 中使用 GradientTape() 计算偏导数时出现问题

标签 python python-3.x tensorflow tensorflow2.0 gradienttape

我在 TensorFlow 中使用自动微分计算梯度时遇到问题。基本上我想创建一个神经网络,它只有一个输出值 f 并获得两个值 (x,t) 的输入。网络应该像一个数学函数一样工作,所以在这种情况下 f(x,t) 其中 x 和 t 是输入变量,我想计算偏导数,例如 df_dx, d2f/dx2df_dt 。稍后我需要这些偏导数来实现特定的损失函数。 这是我的简化代码:

import numpy as np
import tensorflow as tf 
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras import Model


class MyModel(Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.flatten = Flatten(input_shape=(2, 1))
        self.d1 = Dense(28)
        self.f = Dense(1)

    def call(self, y):
        y = self.flatten(y)
        y = self.d1(y)
        y = self.f(y)
        return y

if __name__ == "__main__":

    #inp contains the input-variables (x,t)
    inp = np.random.rand(1,2,1)
    inp_tf = tf.convert_to_tensor(inp, np.float32)   

    #Create a Model
    model = MyModel()

    #Here comes the important part:
    x = inp_tf[0][0]
    t = inp_tf[0][1]

    with tf.GradientTape(persistent=True) as tape:
        tape.watch(inp_tf[0][0])
        tape.watch(inp_tf)
        f = model(inp_tf)

    df_dx = tape.gradient(f, inp_tf[0][0])  #Derivative df_dx
    grad_f = tape.gradient(f, inp_tf)

    tf.print(f)         #--> [[-0.0968768075]]
    tf.print(df_dx)     #--> None
    tf.print(grad_f)    #--> [[[0.284864038]
                        #      [-0.243642956]]]

我期望的是我得到 df_dx = [0.284864038] (grad_f 的第一个组成部分),但结果是 None 。我的问题是:

  1. 是否可以仅对一个输入变量求 f 的偏导数?
  2. 如果是:我必须在代码中更改哪些内容才能使计算 df_dx 不会产生 None

我认为可以做的是修改 class MyModel 的架构我使用两个不同的输入层(一个用于 x,一个用于 t),以便我可以像 f = model(x,t) 这样调用模型但这对我来说似乎不自然,我认为应该有一种更简单的方法。


另一点是,当我将 Flattenlayer 的 input_shape 更改为 self.flatten = Flatten(input_shape=(5,1) 时,我不会收到错误。但我的输入向量有形状(1,2,1),所以我预计会收到错误,但事实并非如此,为什么?我很感谢你的帮助:)


我使用以下配置:

  • 使用 Python 扩展作为 IDE 的 Visual Studio Code
  • Python 版本:3.7.6
  • TensorFlow 版本:2.1.0
  • Keras 版本:2.2.4-tf

最佳答案

每次执行 inp_tf[0][0]inp_tf[0][1] 时,您都会创建一个新张量,但不会使用该新张量作为模型的输入,inp_tf 是。即使 inp_tf[0][0]inp_tf 的一部分,从 TensorFlow 的角度来看,新创建的 inp_tf[0 之间也没有计算图][0]f,因此没有渐变。您必须计算相对于 inp_tf 的梯度,然后从中获取所需的梯度部分。

除此之外,如 tf.GradientTape 的文档所示,您可以使用嵌套磁带来计算二阶导数。并且,如果您使用jacobian ,您可以避免使用persistent=True,这对性能更好。以下是它在您的示例中的工作原理(我将层激活函数更改为 sigmoid,因为默认的线性激活不会有二阶导数)。

import numpy as np
import tensorflow as tf 
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras import Model

class MyModel(Model):
    def __init__(self):
        super(MyModel, self).__init__()
        self.flatten = Flatten(input_shape=(2, 1))
        self.d1 = Dense(28, activation='sigmoid')
        self.f = Dense(1, activation='sigmoid')

    def call(self, y):
        y = self.flatten(y)
        y = self.d1(y)
        y = self.f(y)
        return y

np.random.seed(0)
inp = np.random.rand(1, 2, 1)
inp_tf = tf.convert_to_tensor(inp, np.float32)
model = MyModel()
with tf.GradientTape() as tape:
    tape.watch(inp_tf)
    with tf.GradientTape() as tape2:
        tape2.watch(inp_tf)
        f = model(inp_tf)
    grad_f = tape2.gradient(f, inp_tf)
    df_dx = grad_f[0, 0]
    df_dt = grad_f[0, 1]
j = tape.jacobian(grad_f, inp_tf)
d2f_dx2 = j[0, 0, :, 0, 0]
d2f_dyx = j[0, 0, :, 0, 1]
d2f_dy2 = j[0, 1, :, 0, 1]
d2f_dxy = j[0, 1, :, 0, 0]

tf.print(df_dx)
# [0.0104712956]
tf.print(df_dt)
# [-0.00301733566]
tf.print(d2f_dx2)
# [[-0.000243180315]]
tf.print(d2f_dyx)
# [[-0.000740956515]]
tf.print(d2f_dy2)
# [[1.49392872e-05]]
tf.print(d2f_dxy)
# [[-0.000740956573]]

关于python - 在 TensorFlow2 中使用 GradientTape() 计算偏导数时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61410282/

相关文章:

python - 如何在 Tensorflow 中将小图像添加到大图像中?

python - PJSUA 使用 c 进行 sip 注册时出错

python - 将 Python 列表传递给 WCF 服务

Python C 扩展向异常添加属性

python-3.x - 使用 pyinstaller 将 python 脚本构建为单个 exe

python - 如何同时监控loss和val_loss以避免神经网络对训练集或测试集过度拟合?

python - 如何使文件中的每一行成为包含整个文件的列表的子列表?

python - 在 Python 中看到字符之前,如何删除字符串中的所有内容

python-3.x - 如何告诉 Spacy 不要使用 retokenizer 将任何单词与撇号分开?

python - Tensorflow 将预测值转换为二进制