在Keras / Tensorflow自定义损失函数中使用其他**可**变量

问题描述

我知道如何在Keras中使用附加输入而不是标准的y_truey_pred对来编写自定义损失函数,请参见下文。我的问题是使用 trainable 变量(其中一些)输入损失函数,该变量是损失梯度的一部分,因此应进行更新。

我的解决方法是:

  • 在网络中输入N X V大小的虚拟输入,其中N是观测值的数量V是其他变量的数量
  • 添加一个Dense()dummy_output,以便Keras跟踪我的V的“权重”
  • 在我的自定义损失函数中为我的真实输出层使用该层的V权重
  • 为此dummy_output图层使用虚拟损失函数(仅返回0.0和/或权重为0.0),因此我的V“权重”仅通过我的自定义损失函数进行更新

我的问题是:是否有更自然的类似Keras / TF的方法?因为它是如此的伪造,更不用说容易出错了。

我的解决方法示例:

(是的,我知道这是一个非常愚蠢的自定义损失函数,实际上情况要复杂得多)

import numpy as np
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Dense
from tensorflow.keras.callbacks import EarlyStopping
import tensorflow.keras.backend as K
from tensorflow.keras.layers import Input
from tensorflow.keras import Model

n_col = 10
n_row = 1000
X = np.random.normal(size=(n_row,n_col))
beta = np.arange(10)
y = X @ beta

X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.2,random_state=42)

# my custom loss function accepting my dummy layer with 2 variables
def custom_loss_builder(dummy_layer):
    def custom_loss(y_true,y_pred):
        var1 = dummy_layer.trainable_weights[0][0]
        var2 = dummy_layer.trainable_weights[0][1]
        return var1 * K.mean(K.square(y_true-y_pred)) + var2 ** 2 # so var2 should get to zero,var1 should get to minus infinity?
    return custom_loss

# my dummy loss function
def dummy_loss(y_true,y_pred):
    return 0.0

# my dummy input,N X V,where V is 2 for 2 vars
dummy_x_train = np.random.normal(size=(X_train.shape[0],2)) 

# model
inputs = Input(shape=(X_train.shape[1],))
dummy_input = Input(shape=(dummy_x_train.shape[1],))
hidden1 = Dense(10)(inputs) # here only 1 hidden layer in the "real" network,assume whatever network is built here
output = Dense(1)(hidden1)
dummy_output = Dense(1,use_bias=False)(dummy_input)
model = Model(inputs=[inputs,dummy_input],outputs=[output,dummy_output])

# compilation,notice zero loss for the dummy_output layer
model.compile(
  loss=[custom_loss_builder(model.layers[-1]),dummy_loss],loss_weights=[1.0,0.0],optimizer= 'adam')

# run,notice y_train repeating for dummy_output layer,it will not be used,Could have created dummy_y_train as well
history = model.fit([X_train,dummy_x_train],[y_train,y_train],batch_size=32,epochs=100,validation_split=0.1,verbose=0,callbacks=[EarlyStopping(monitor='val_loss',patience=5)])

无论他们想向var1和{{1}求值,var2dummy_output的初始值(inf层的初始化)似乎都可以工作}:

(此图来自迭代运行模型并保存以下两个权重)

0

enter image description here

解决方法

在这里回答我自己的问题,经过几天的努力,我在没有虚拟输入的情况下就可以使用它,我认为这要好得多,并且应该是“规范”的方式,直到Keras / TF简化流程为止。 Keras / TF文档here就是这样。

将损失函数与外部 trainable 变量一起使用的关键是通过使用自定义的损失/输出 Layer ,其{{1 }}实现,如下所示:

self.add_loss(...)

现在请注意,call()层需要两个输入,即实际的class MyLoss(Layer): def __init__(self,var1,var2): super(MyLoss,self).__init__() self.var1 = K.variable(var1) # or tf.Variable(var1) etc. self.var2 = K.variable(var2) def get_vars(self): return self.var1,self.var2 def custom_loss(self,y_true,y_pred): return self.var1 * K.mean(K.square(y_true-y_pred)) + self.var2 ** 2 def call(self,y_pred): self.add_loss(self.custom_loss(y_true,y_pred)) return y_pred 和预测的MyLoss,直到这一点为止:

y_true

最后,如TF文档所述,在这种情况下,您不必在y函数中指定inputs = Input(shape=(X_train.shape[1],)) y_input = Input(shape=(1,)) hidden1 = Dense(10)(inputs) output = Dense(1)(hidden1) my_loss = MyLoss(0.5,0.5)(y_input,output) # here can also initialize those var1,var2 model = Model(inputs=[inputs,y_input],outputs=my_loss) model.compile(optimizer= 'adam') loss

y

同样,请注意,fit()作为输入之一进入history = model.fit([X_train,y_train],None,batch_size=32,epochs=100,validation_split=0.1,verbose=0,callbacks=[EarlyStopping(monitor='val_loss',patience=5)])

现在可以使用了:

y_train

enter image description here

(我还要提到fit()var1_list = [] var2_list = [] for i in range(100): if i % 10 == 0: print('step %d' % i) model.fit([X_train,epochs=1,verbose=0) var1,var2 = model.layers[-1].get_vars() var1_list.append(var1.numpy()) var2_list.append(var2.numpy()) plt.plot(var1_list,label='var1') plt.plot(var2_list,'r',label='var2') plt.legend() plt.show() 的特定模式在很大程度上取决于其初始值,如果var1的初始值大于1,则实际上不会降低直到负var2