在機器學習中,會遇到拉普拉斯方程問題。如
$$-\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.]]