代码之家  ›  专栏  ›  技术社区  ›  benbotto

附加动态参数的Keras损失函数

  •  12
  • benbotto  · 技术社区  · 6 年前

    我正在为deep-q网络实现优先体验重播,规范的一部分是将梯度乘以所谓的重要性抽样(is)权重。下文第3.4节讨论了梯度修正: https://arxiv.org/pdf/1511.05952.pdf 我正在努力创建一个自定义损失函数,除了 y_true y_pred

    以下是我的模型的简化版本:

    import numpy as np
    import tensorflow as tf
    
    # Input is RAM, each byte in the range of [0, 255].
    in_obs = tf.keras.layers.Input(shape=(4,))
    
    # Normalize the observation to the range of [0, 1].
    norm = tf.keras.layers.Lambda(lambda x: x / 255.0)(in_obs)
    
    # Hidden layers.
    dense1 = tf.keras.layers.Dense(128, activation="relu")(norm)
    dense2 = tf.keras.layers.Dense(128, activation="relu")(dense1)
    dense3 = tf.keras.layers.Dense(128, activation="relu")(dense2)
    dense4 = tf.keras.layers.Dense(128, activation="relu")(dense3)
    
    # Output prediction, which is an action to take.
    out_pred = tf.keras.layers.Dense(2, activation="linear")(dense4)
    
    opt     = tf.keras.optimizers.Adam(lr=5e-5)
    network = tf.keras.models.Model(inputs=in_obs, outputs=out_pred)
    network.compile(optimizer=opt, loss=huber_loss_mean_weighted)
    

    这是我的自定义损失函数,它只是Huber损失乘以is权重的一个实现:

    '''
     ' Huber loss: https://en.wikipedia.org/wiki/Huber_loss
    '''
    def huber_loss(y_true, y_pred):
      error = y_true - y_pred
      cond  = tf.keras.backend.abs(error) < 1.0
    
      squared_loss = 0.5 * tf.keras.backend.square(error)
      linear_loss  = tf.keras.backend.abs(error) - 0.5
    
      return tf.where(cond, squared_loss, linear_loss)
    
    '''
     ' Importance Sampling weighted huber loss.
    '''
    def huber_loss_mean_weighted(y_true, y_pred, is_weights):
      error = huber_loss(y_true, y_pred)
    
      return tf.keras.backend.mean(error * is_weights)
    

    重要的是 is_weights 是动态的,即每次都不同 fit() 被调用。因此,我不能简单地关闭 is\U重量 如下所述: Make a custom loss function in keras

    我在网上找到了此代码,它似乎使用了 Lambda 计算损失的图层: https://github.com/keras-team/keras/blob/master/examples/image_ocr.py#L475 这看起来很有希望,但我正在努力理解它/使它适应我的特定问题。非常感谢您的帮助。

    1 回复  |  直到 6 年前
        1
  •  29
  •   pitfall    6 年前

    好啊这里有一个例子。

    from keras.layers import Input, Dense, Conv2D, MaxPool2D, Flatten
    from keras.models import Model
    from keras.losses import categorical_crossentropy
    
    def sample_loss( y_true, y_pred, is_weight ) :
        return is_weight * categorical_crossentropy( y_true, y_pred ) 
    
    x = Input(shape=(32,32,3), name='image_in')
    y_true = Input( shape=(10,), name='y_true' )
    is_weight = Input(shape=(1,), name='is_weight')
    f = Conv2D(16,(3,3),padding='same')(x)
    f = MaxPool2D((2,2),padding='same')(f)
    f = Conv2D(32,(3,3),padding='same')(f)
    f = MaxPool2D((2,2),padding='same')(f)
    f = Conv2D(64,(3,3),padding='same')(f)
    f = MaxPool2D((2,2),padding='same')(f)
    f = Flatten()(f)
    y_pred = Dense(10, activation='softmax', name='y_pred' )(f)
    model = Model( inputs=[x, y_true, is_weight], outputs=y_pred, name='train_only' )
    model.add_loss( sample_loss( y_true, y_pred, is_weight ) )
    model.compile( loss=None, optimizer='sgd' )
    print model.summary()
    

    注意,由于您通过 add_loss() ,您不必通过 compile( loss=xxx )

    关于训练模特,除了你的动作,没有什么特别的 y_true 到您的输入端。见下文

    import numpy as np 
    a = np.random.randn(8,32,32,3)
    a_true = np.random.randn(8,10)
    a_is_weight = np.random.randint(0,2,size=(8,1))
    model.fit( [a, a_true, a_is_weight] )
    

    最后,您可以创建一个测试模型(在 model )便于使用,即。

    test_model = Model( inputs=x, outputs=y_pred, name='test_only' )
    a_pred = test_model.predict( a )