问题描述
我知道如何在Keras中使用附加输入而不是标准的y_true
,y_pred
对来编写自定义损失函数,请参见下文。我的问题是使用 trainable 变量(其中一些)输入损失函数,该变量是损失梯度的一部分,因此应进行更新。
我的解决方法是:
- 在网络中输入
N
XV
大小的虚拟输入,其中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}求值,var2
和dummy_output
的初始值(inf
层的初始化)似乎都可以工作}:
(此图来自迭代运行模型并保存以下两个权重)
0
解决方法
在这里回答我自己的问题,经过几天的努力,我在没有虚拟输入的情况下就可以使用它,我认为这要好得多,并且应该是“规范”的方式,直到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
(我还要提到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
)