在 TensorFlow2 中使用 GradientTape() 计算偏导数的问题
Problem computing partial derivatives with GradientTape() in TensorFlow2
我在使用 TensorFlow 中的自动微分计算梯度时遇到问题。基本上我想创建一个只有一个输出值 f 并获得两个值 (x,t) 输入的神经网络。网络应该像一个数学函数一样工作,所以在这种情况下 f(x,t) 其中 x 和 t 是输入变量,我想计算偏导数,例如 df_dx, d2f/dx2
或 df_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
。我的问题是:
- 是否可以只对一个输入变量求 f 的偏导数?
- 如果是:我必须在我的代码中更改哪些计算 df_dx 没有结果
None
?
我认为可以做的是修改 class MyModel
的架构,我使用两个不同的输入层(一个用于 x,一个用于 t),这样我就可以像 f = model(x,t)
一样调用模型但这对我来说似乎不自然,我认为应该有更简单的方法。
另一点是,当我将 Flattenlayer 的 input_shape 更改为 self.flatten = Flatten(input_shape=(5,1)
时,我没有收到错误消息,但我的输入向量具有形状 (1,2,1),所以我希望得到一个错误,但事实并非如此,为什么?感谢您的帮助:)
我使用以下配置:
- Visual Studio 代码 Python-扩展名为 IDE
- 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]
if part of inp_tf
,从 TensorFlow 的角度来看,你新建的 inp_tf[0][0]
和 f
之间没有计算图,因此没有梯度。您必须计算相对于 inp_tf
的梯度,然后从那里获取您想要的梯度部分。
除此之外,如 tf.GradientTape
, you can use nested tapes to compute second order derivatives. And, if you use the 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]]
我在使用 TensorFlow 中的自动微分计算梯度时遇到问题。基本上我想创建一个只有一个输出值 f 并获得两个值 (x,t) 输入的神经网络。网络应该像一个数学函数一样工作,所以在这种情况下 f(x,t) 其中 x 和 t 是输入变量,我想计算偏导数,例如 df_dx, d2f/dx2
或 df_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
。我的问题是:
- 是否可以只对一个输入变量求 f 的偏导数?
- 如果是:我必须在我的代码中更改哪些计算 df_dx 没有结果
None
?
我认为可以做的是修改 class MyModel
的架构,我使用两个不同的输入层(一个用于 x,一个用于 t),这样我就可以像 f = model(x,t)
一样调用模型但这对我来说似乎不自然,我认为应该有更简单的方法。
另一点是,当我将 Flattenlayer 的 input_shape 更改为 self.flatten = Flatten(input_shape=(5,1)
时,我没有收到错误消息,但我的输入向量具有形状 (1,2,1),所以我希望得到一个错误,但事实并非如此,为什么?感谢您的帮助:)
我使用以下配置:
- Visual Studio 代码 Python-扩展名为 IDE
- 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]
if part of inp_tf
,从 TensorFlow 的角度来看,你新建的 inp_tf[0][0]
和 f
之间没有计算图,因此没有梯度。您必须计算相对于 inp_tf
的梯度,然后从那里获取您想要的梯度部分。
除此之外,如 tf.GradientTape
, you can use nested tapes to compute second order derivatives. And, if you use the 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]]