在机器学习中,会遇到拉普拉斯方程问题。如
$$-\Delta u=f$$
这里 是一个关于多变量的函数,譬如 . 那么
$$\Delta u = \frac{\partial^2 u}{\partial x^2}+\frac{\partial^2 u}{\partial y^2}$$.
在tensorflow中单值输出关于多元变量的高阶导数是如何计算的呢? 本文为我的个人测试,如有不合适的地方,欢迎指正.
-
一阶偏导的计算
例一
$$u = 3(x^3+y^3)-6(x^2+y^2)$$
我们很显然知道:.那么程序结果呢?
def test_gradient0():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x1 = tf.Variable(3, dtype=tf.float32, name='x_1')
x2 = tf.Variable(1, dtype=tf.float32, name='x_2')
y = 3 * (tf.pow(x1, 3)+tf.pow(x2, 3)) - 6*(tf.pow(x1, 2)+tf.pow(x2, 2))
dx1, dx2 = tf.gradients(y, [x1, x2]) # 一阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx11, dx22 = sess.run([dx1, dx2], feed_dict={x1: 1, x2: 1})
print(dx11)
print(dx22)
print('函数y在x_1=1, x_2=1处的关于x_1的一阶导数:dx1=', dx11, ',关于x_2的一阶导数:dx2=', dx22, sep='')
输出结果:
函数y在x_1=1, x_2=1处的关于x_1的一阶导数:dx1=-3.0,关于x_2的一阶导数:dx2=-3.0
例二
$$u = 3(x^3+y^3)-6(x^2+y^2)+xy$$
我们很显然知道:.那么程序结果呢?
def test_gradient1():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x1 = tf.Variable(3, dtype=tf.float32, name='x_1')
x2 = tf.Variable(1, dtype=tf.float32, name='x_2')
y = 3 * (tf.pow(x1, 3)+tf.pow(x2, 3)) - 6*(tf.pow(x1, 2)+tf.pow(x2, 2)) + x1*x2
dx1, dx2 = tf.gradients(y, [x1, x2]) # 一阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx11, dx22 = sess.run([dx1, dx2], feed_dict={x1: 1, x2: 1})
print('函数y在x_1=1, x_2=1处的关于x_1的一阶导数:dx1=', dx11, ',关于x_2的一阶导数:dx2=', dx22, sep='')
输出结果:
函数y在x_1=1, x_2=1处的关于x_1的一阶导数:dx1=-2.0,关于x_2的一阶导数:dx2=-2.0
二阶偏导的计算
$$u = 3(x^3+y^3)-6(x^2+y^2)$$
我们很显然知道:.
def test_gradient2():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x1 = tf.Variable(3, dtype=tf.float32, name='x_1')
x2 = tf.Variable(1, dtype=tf.float32, name='x_2')
y = 3 * (tf.pow(x1, 3)+tf.pow(x2, 3)) - 6*(tf.pow(x1, 2)+tf.pow(x2, 2))
dx1, dx2 = tf.gradients(y, [x1, x2]) # 一阶导数
ddx1, ddx2 = tf.gradients([dx1, dx2], [x1, x2]) # 一阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
ddx11, ddx22 = sess.run([ddx1, ddx2], feed_dict={x1: 1, x2: 1})
print('函数y在x_1=1, x_2=1处的关于x_1的一阶导数:dx1=', ddx11, ',关于x_2的一阶导数:dx2=', ddx22, sep='')
函数y在x_1=1, x_2=1处的关于x_1的一阶导数:dx1=6.0,关于x_2的一阶导数:dx2=6.0
$$u = 3(x^3+y^3)-6(x^2+y^2)+xy$$
我们很显然知道:.
def test_gradient3():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x1 = tf.Variable(3, dtype=tf.float32, name='x_1')
x2 = tf.Variable(1, dtype=tf.float32, name='x_2')
y = 3 * (tf.pow(x1, 3)+tf.pow(x2, 3)) - 6*(tf.pow(x1, 2)+tf.pow(x2, 2))+x*y
dx1, dx2 = tf.gradients(y, [x1, x2]) # 一阶导数
ddx1, ddx2 = tf.gradients([dx1, dx2], [x1, x2]) # 一阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
ddx11, ddx22 = sess.run([ddx1, ddx2], feed_dict={x1: 1, x2: 1})
print('函数y在x_1=1, x_2=1处的关于x_1的二阶导数:ddx1=', ddx11, ',关于x_2的二阶导数:ddx2=', ddx22, sep='')
$$u = 3(x^3+y^3)-6(x^2+y^2)+x^2y^2$$
我们很显然知道:.
def test_gradient4():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x1 = tf.Variable(3, dtype=tf.float32, name='x_1')
x2 = tf.Variable(1, dtype=tf.float32, name='x_2')
y = 3 * (tf.pow(x1, 3)+tf.pow(x2, 3)) - 6*(tf.pow(x1, 2)+tf.pow(x2, 2)) + (x1**2)*(x2**2)
dx1, dx2 = tf.gradients(y, [x1, x2]) # 一阶导数
ddx1, ddx2 = tf.gradients([dx1, dx2], [x1, x2]) # 一阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
ddx11, ddx22 = sess.run([ddx1, ddx2], feed_dict={x1: 1, x2: 1})
print('函数y在x_1=1, x_2=1处的关于x_1的二阶导数:ddx1=', ddx11, ',关于x_2的二阶导数:ddx2=', ddx22, sep='')
划重点的来了,以上的变量都是独立的。如果变量是向量呢?是什么样子的。我们首先看一下,函数对向量的求导机制. 这里 .那么
参考:机器学习中常用的矩阵求导公式 https://blog.csdn.net/xtydtc/article/details/51133903
https://blog.csdn.net/daaikuaichuan/article/details/80620518
这里 代表激活函数,如Relu。并且
u的形式为(假设计算过程中值均为正值)
def test_gradient6():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x = tf.Variable([[0.1], [0.1]], dtype=tf.float32, name='x')
W1 = np.array([[1, 2], [3, 4]], dtype=np.float32)
B1 = np.array([[1], [3]], dtype=np.float32)
W2 = np.array([[1, 2], [3, 4]], dtype=np.float32)
B2 = np.array([[2], [4]], dtype=np.float32)
W3 = np.array([[2, 1]], dtype=np.float32)
B3 = np.array([[2]], dtype=np.float32)
y = tf.nn.relu(tf.matmul(W1, x) + B1)
y = tf.nn.relu(tf.matmul(W2, y) + B2)
y = tf.nn.relu(tf.matmul(W3, y) + B3)
dx = tf.gradients(y, x) # 一阶导数
ddx = tf.gradients(dx, x) # 二阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx1, ddx1 = sess.run([dx, ddx], feed_dict={x: [[1], [1]]})
print('函数y在x=[1,1]处的一阶导数:dx=', dx1, ',关于x的二阶导数:ddx=', ddx1, sep='')
函数y在x=[1,1]处的一阶导数:dx=[array([[29.],
[42.]], dtype=float32)],关于x的二阶导数:ddx=[array([[0.],
[0.]], dtype=float32)]
向量对向量求导。
首先看一些概念。
例子
$$u = 3z^3-6z^2+z, z=(x,y)^T$$
我么有
记 。那么
这里需要注意, 是一个关于 的常量,所以是标量对向量求导。结果应该是, 分开的。最后排列成超向量或者矩阵都可以。如:
,
def test_gradient5():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x = tf.Variable([[0.1], [0.1]], dtype=tf.float32, name='x')
y = 3*tf.pow(x, 3) - 6 * tf.pow(x, 2) + x
dx = tf.gradients(y, x) # 一阶导数
ddx = tf.gradients(dx, x) # 二阶导数
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx1, ddx1 = sess.run([dx, ddx], feed_dict={x: [[1], [1]]})
print('函数y在x=[1;1]处的一阶导数:dx=', dx1, ',关于x的二阶导数:ddx=', ddx1, sep='')
运行结果. 函数y在x=[1;1]处的一阶导数:dx=[array([[-2.],
[-2.]], dtype=float32)],关于x的二阶导数:ddx=[array([[6.],
[6.]], dtype=float32)]
这里有个问题,tensorflow中向量对向量求导(行对行,列对列)貌似有问题。留作慢慢研究。。。。。。。
下面看看如何实现 拉普拉斯 的二阶偏导。我们以简单的函数为例:
一阶导数为
二阶导数为
def test_gradient7():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x = tf.Variable([[0.1], [0.1]], dtype=tf.float32, name='x')
W1 = np.array([[1, 2], [3, 4]], dtype=np.float32)
W2 = np.array([[1, 1]], dtype=np.float32)
y = x*tf.matmul(W1, x)
y = tf.matmul(W2, y)
Dx = tf.gradients(y, x)[0] # 一阶导数
Dx1 = tf.gather(Dx, [0], axis=0) # 将一阶导数关于x1的部分取出来
Dx2 = tf.gather(Dx, [1], axis=0) # 将一阶导数关于x2的部分取出来
DDx1 = tf.gradients(Dx1, x) # 对取出来额部分关于x求导,就可以得到关于x的二阶导数的向量
DDx1 = tf.squeeze(DDx1)
DDx1 = tf.gather(DDx1, [0]) # 将一阶导数关于x2的部分取出来
DDx2 = tf.gradients(Dx2, x) # 对取出来额部分关于x求导,就可以得到关于x的二阶导数的向量
DDx2 = tf.squeeze(DDx2)
DDx2 = tf.gather(DDx2, [1]) # 将一阶导数关于x1的部分取出来
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx, dx1, dx2, ddx1, ddx2 = sess.run([Dx, Dx1, Dx2, DDx1, DDx2], feed_dict={x: [[1], [1]]})
print('dx:', dx)
print('dx1:', dx1)
print('dx2:', dx2)
print('ddx1:', ddx1)
print('ddx2:', ddx2)
运行结果:
dx:
[[ 7.]
[13.]]
dx1: [[7.]]
dx2: [[13.]]
ddx1: [2.]
ddx2: [8.]
求二阶导数的另一种方式
def test_gradient8():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x1 = tf.Variable([[0.1]], dtype=tf.float32, name='x1')
x2 = tf.Variable([[0.1]], dtype=tf.float32, name='x1')
x = tf.concat([x1, x2], axis=0)
W1 = np.array([[1, 2], [3, 4]], dtype=np.float32)
W2 = np.array([[1, 1]], dtype=np.float32)
y = x*tf.matmul(W1, x)
y = tf.matmul(W2, y)
Dx = tf.gradients(y, x)[0] # 一阶导数
Dx1 = tf.gradients(y, x1)[0]
DDx1 = tf.gradients(Dx1, x1)[0] # 对取出来额部分关于x求导,就可以得到关于x的二阶导数的向量
Dx2 = tf.gradients(y, x2)[0]
DDx2 = tf.gradients(Dx2, x2)[0] # 对取出来额部分关于x求导,就可以得到关于x的二阶导数的向量
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx, dx1, dx2, ddx1, ddx2 = sess.run([Dx, Dx1, Dx2, DDx1, DDx2], feed_dict={x: [[1], [1]]})
print('dx:\n', dx)
print('dx1:', dx1)
print('dx2:', dx2)
print('ddx1:', ddx1)
print('ddx2:', ddx2)
结果:
dx:
[[ 7.]
[13.]]
dx1: [[7.]]
dx2: [[13.]]
ddx1: [[2.]]
ddx2: [[8.]]
对于多维输入,结果也类似。代码如下:
def test_gradient9():
with tf.device('/gpu:%s' % 0):
with tf.variable_scope('vscope', reuse=tf.AUTO_REUSE):
# 注意类型必须均为浮点型,并且一致
# 变量
x = tf.placeholder(tf.float32, name='X_it', shape=[2, 3])
# x = tf.Variable([[0.1, 0.1, 0,1], [0.1, 0.1, 0,1]], dtype=tf.float32, name='x')
W1 = np.array([[1, 2], [3, 4]], dtype=np.float32)
W2 = np.array([[1, 1]], dtype=np.float32)
y = x*tf.matmul(W1, x)
y = tf.matmul(W2, y)
Dx = tf.gradients(y, x)[0] # 一阶导数
Dx1 = tf.gather(Dx, [0], axis=0) # 将一阶导数关于x1的部分取出来
Dx2 = tf.gather(Dx, [1], axis=0) # 将一阶导数关于x2的部分取出来
DDx1 = tf.gradients(Dx1, x) # 对取出来额部分关于x求导,就可以得到关于x的二阶导数的向量
DDx1 = tf.squeeze(DDx1)
DDx1 = tf.gather(DDx1, [0], axis=0) # 将一阶导数关于x2的部分取出来
DDx2 = tf.gradients(Dx2, x) # 对取出来额部分关于x求导,就可以得到关于x的二阶导数的向量
DDx2 = tf.squeeze(DDx2)
DDx2 = tf.gather(DDx2, [1], axis=0) # 将一阶导数关于x1的部分取出来
# ConfigProto 加上allow_soft_placement=True就可以使用 gpu 了
config = tf.ConfigProto(allow_soft_placement=True) # 创建sess的时候对sess进行参数配置
config.gpu_options.allow_growth = True # True是让TensorFlow在运行过程中动态申请显存,避免过多的显存占用。
config.allow_soft_placement = True # 当指定的设备不存在时,允许选择一个存在的设备运行。比如gpu不存在,自动降到cpu上运行
with tf.Session(config=config) as sess:
sess.run(tf.global_variables_initializer())
dx, dx1, dx2, ddx1, ddx2 = sess.run([Dx, Dx1, Dx2, DDx1, DDx2], feed_dict={x: [[1, 1, 1], [1, 1, 1]]})
print('dx:\n', dx)
print('dx1:', dx1)
print('dx2:', dx2)
print('ddx1:', ddx1)
print('ddx2:', ddx2)
运行结果:
dx:
[[ 7. 7. 7.]
[13. 13. 13.]]
dx1: [[7. 7. 7.]]
dx2: [[13. 13. 13.]]
ddx1: [[2. 2. 2.]]
ddx2: [[8. 8. 8.]]