【问题标题】:Derivatives of n-dimensional function in KerasKeras中n维函数的导数
【发布时间】:2018-04-26 09:56:55
【问题描述】:

假设我有一个二元函数,例如:z = x^2 + y^2。 I learned 在 Keras 上我可以使用 Lambda 层计算 n 阶导数:

def bivariate_function(x, y):

    x2 = Lambda(lambda u: K.pow(u,2))(x)
    y3 = Lambda(lambda u: K.pow(u,2))(y)

    return Add()([x2,y3])

def derivative(y,x):
    return Lambda(lambda u: K.gradients(u[0],u[1]))([y,x])

f = bivariate_function(x,y)
df_dx = grad(f,x)      # 1st derivative wrt to x
df_dy = grad(f,y)      # 1st derivative wrt to y
df_dx2 = grad(df_dx,x) # 2nd derivative wrt to x
df_dy2 = grad(df_dy,y) # 2nd derivative wrt to y

但是,如何将这种方法应用于损失函数中输入的 NN 输出导数?我不能(?)只是简单地将两个输入输入到密集层(如上面创建的那些)。

例如,尝试使用 Input(shape=(2,)) 将第一个变量的一阶导数和第二个变量的二阶导数的总和用作损失(即 d/dx+d²/dy²),我设法到达这里:

import tensorflow as tf
from keras.models import *
from keras.layers import *
from keras import backend as K

def grad(f, x):
    return Lambda(lambda u: K.gradients(u[0], u[1]), output_shape=[2])([f, x])

def custom_loss(input_tensor,output_tensor):
    def loss(y_true, y_pred):

        df1 = grad(output_tensor,input_tensor)
        df2 = grad(df1,input_tensor)
        df = tf.add(df1[0,0],df2[0,1])      

        return df
    return loss

input_tensor = Input(shape=(2,))
hidden_layer = Dense(100, activation='relu')(input_tensor)
output_tensor = Dense(1, activation='softplus')(hidden_layer)

model = Model(input_tensor, output_tensor)
model.compile(loss=custom_loss(input_tensor,output_tensor), optimizer='sgd')

xy = np.mgrid[-3.0:3.0:0.1, -3.0:3.0:0.1].reshape(2,-1).T
model.fit(x=xy,y=xy, batch_size=10, epochs=100, verbose=2)

但感觉就像我没有以正确的方式做这件事。更糟糕的是,在第一个时代之后,我得到的只是nan's。

【问题讨论】:

  • 无法运行您的代码。您将grad() 函数重命名为derivative(),但调用仍为grad()。另外,您如何从input_tensor 获得x。能否请您发布一个可运行的一体式代码,我只需复制粘贴即可查看发生了什么?
  • @PeterSzoldan 刚刚用一个有效的 sn-p 进行了编辑!感谢您的帮助!

标签: python tensorflow neural-network keras


【解决方案1】:

这里的主要问题是理论上的。

您正在尝试最小化 doutput_tensor/dx + d2 sup>output_tensor/d2x。但是,您的网络只是将输入 x-s 与 relusoftplus 激活线性组合。好吧,softplus 给它增加了一点扭曲,但它也有一个单调递增的导数。因此,为了使导数尽可能小,网络将使用负权重尽可能地放大输入,以使导数尽可能小(即,一个非常大的负数),在某个点达到NaN。我将第一层减少到 5 个神经元并运行模型 2 个 epoch,权重变为:

('dense_1',
[数组([[ 1.0536456 , -0.32706773, 0.0072904 , 0.01986691, 0.9854533 ],
[-0.3242108 , -0.56753945, 0.8098554 , -0.7545874 , 0.2716419 ]],
dtype=float32),
数组([0.01207507,0.09927677,-0.01768671,-0.12874101,0.0210707], dtype=float32)])

('dense_2', [数组([[-0.4332278],[0.6621602],[-0.07802075],[-0.5798264],[-0.40561703]],
dtype=float32),
数组([0.11167384], dtype=float32)])

您可以看到第二层保持负号,而第一层为正号,反之亦然。 (偏差没有任何梯度,因为它们对导数没有贡献。好吧,由于softplus 不完全正确,但或多或​​少。)

因此,您必须提出一个不会向极端参数值发散的损失函数,因为这将无法训练,它只会增加权重值直到它们 NaN

这是我运行的版本:

import tensorflow as tf
from keras.models import *
from keras.layers import *
from keras import backend as K

def grad(f, x):
    return Lambda(lambda u: K.gradients(u[0], u[1]), output_shape=[2])([f, x])

def ngrad(f, x, n):
    if 0 == n:
        return f
    else:
        return Lambda(lambda u: K.gradients(u[0], u[1]), output_shape=[2])([ngrad( f, x, n - 1 ), x])

def custom_loss(input_tensor,output_tensor):
    def loss(y_true, y_pred):

        _df1 = grad(output_tensor,input_tensor)
        df1 = tf.Print( _df1, [ _df1 ], message = "df1" )
        _df2 = grad(df1,input_tensor)
        df2 = tf.Print( _df2, [ _df2 ], message = "df2" )
        df = tf.add(df1,df2)      

        return df
    return loss

input_tensor = Input(shape=(2,))
hidden_layer = Dense(5, activation='softplus')(input_tensor)
output_tensor = Dense(1, activation='softplus')(hidden_layer)

model = Model(input_tensor, output_tensor)
model.compile(loss=custom_loss(input_tensor,output_tensor), optimizer='sgd')

xy = np.mgrid[-3.0:3.0:0.1, -3.0:3.0:0.1].reshape( 2, -1 ).T
#print( xy )
model.fit(x=xy,y=xy, batch_size=10, epochs=2, verbose=2)
for layer in model.layers: print(layer.get_config()['name'], layer.get_weights())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-21
    • 2015-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多