tensorflow多元函數高階求導

在機器學習中,會遇到拉普拉斯方程問題。如

 $$-\Delta u=f$$

這裏 u 是一個關於多變量的函數,譬如 u=u(x,y). 那麼

$$\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)$$

我們很顯然知道:u_x = 9x^2-12x, u_y = 9y^2-12y.那麼程序結果呢?

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$$

我們很顯然知道:u_x = 9x^2-12x+y, u_y = 9y^2-12y+x.那麼程序結果呢?

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)$$

我們很顯然知道:u_{xx} = 18x-12, u_{yy} = 18y-12.

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$$

我們很顯然知道:u_{xx} = 18x-12, u_{yy} = 18y-12.

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$$

我們很顯然知道:u_{xx} = 18x-12+2y^2, u_{yy} = 18y-12+2x^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='')

劃重點的來了,以上的變量都是獨立的。如果變量是向量呢?是什麼樣子的。我們首先看一下,函數對向量的求導機制. f(\boldsymbol{x}) =f(x_1,x_2,x_3,\cdots,x_n),這裏 \boldsymbol{x}=(x_1,x_2,x_3,\cdots,x_n)'.那麼

\frac{df(\boldsymbol{x})}{d\boldsymbol{x}} = \left ( \frac{\partial f}{\partial x_1},\frac{\partial f}{\partial x_2},\frac{\partial f}{\partial x_3},\cdots,\frac{\partial f}{\partial x_n}\right )'

參考:機器學習中常用的矩陣求導公式 https://blog.csdn.net/xtydtc/article/details/51133903

https://blog.csdn.net/daaikuaichuan/article/details/80620518

u=\sigma (W_2*\sigma (W_1*z+B_1)+B_2),z=(x,y)'

這裏 \sigma 代表激活函數,如Relu。並且

W_1 =\left[ \begin{matrix} 1&2\\3&4 \end{matrix} \right], W_2 =\left[ \begin{matrix} 1&2\\3&4 \end{matrix} \right],W_3 =\left[2,1 \right], B_1 =\left[ \begin{matrix} 1\\3 \end{matrix} \right], B_2 =\left[ \begin{matrix} 2\\4 \end{matrix} \right], B_3 =\left[2 \right]

u的形式爲(假設計算過程中值均爲正值)

u=[2,1]*\left [ \begin{matrix} (x+2y+1)+2(3x+4y+3)+2\\ 3(x+2y+1)+4(3x+4y+3)+4 \end{matrix} \right ]+ 2= [2,1]*\left [ \begin{matrix} 7x+10y+9\\ 15x+22y+19 \end{matrix} \right ]=(7x+10y+9)+2(15x+22y+19)+2

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$$

我麼有 

u = 3\left[ \begin{matrix} x^3\\ y^3 \end{matrix}\right ]- 6\left[ \begin{matrix} x^2\\ y^2 \end{matrix}\right ]+ \left[ \begin{matrix} x\\ y \end{matrix}\right ]= \left [ \begin{matrix} 3x^3-6x^2+x\\ 3y^3-6y^2+y \end{matrix} \right ]

u_1 = 3x^3-6x^2+x, u_2=3y^3-6y^2+y。那麼

\frac{d u}{d z} = \frac{d\left (\begin{matrix} u_1\\u_2 \end{matrix} \right )}{d \left (\begin{matrix} x\\y \end{matrix} \right )} =\frac{d\left[ \begin{matrix} 9x^2-12x+1\\ 9y^2-12y+1 \end{matrix}\right ]}{d\left (\begin{matrix} x\\y \end{matrix} \right )}

這裏需要注意,u_1 是一個關於 (x,y)'的常量,所以是標量對向量求導。結果應該是u_1u_2 分開的。最後排列成超向量或者矩陣都可以。如:

\left ( \begin{matrix} \frac{du_1}{dx}\\ \frac{du_1}{dy} \\ \frac{du_2}{dx} \\ \frac{du_2}{dy} \end{matrix} \right ),   \left ( \begin{matrix} \frac{du_1}{dx}& \frac{du_1}{dy} \\ \frac{du_2}{dx} &\frac{du_2}{dy} \end{matrix} \right )

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中向量對向量求導(行對行,列對列)貌似有問題。留作慢慢研究。。。。。。。

下面看看如何實現 拉普拉斯 的二階偏導。我們以簡單的函數爲例:

u = x^2+5xy+4y^2

一階導數爲 u_x = 2x+5y,u_y=8y+5x

二階導數爲 u_{xx}=2,u_{yy}=8

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.]]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章