【发布时间】:2021-01-21 05:13:12
【问题描述】:
我知道如何在 Keras 中编写带有附加输入的自定义损失函数,而不是标准的 y_true、y_pred 对,见下文。我的问题是使用 trainable 变量(其中一些)输入损失函数,该变量是损失梯度的一部分,因此应该更新。
我的解决方法是:
- 在网络中输入
NXV大小的虚拟输入,其中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 和var2(dummy_output 层的初始化)的起始值似乎都可以分别减去inf 和0:
(此图来自迭代运行模型并保存这两个权重,如下所示)
var1_list = []
var2_list = []
for i in range(100):
if i % 10 == 0:
print('step %d' % i)
model.fit([X_train, dummy_x_train], [y_train, y_train],
batch_size=32, epochs=1, validation_split=0.1, verbose=0)
var1, var2 = model.layers[-1].get_weights()[0]
var1_list.append(var1.item())
var2_list.append(var2.item())
plt.plot(var1_list, label='var1')
plt.plot(var2_list, 'r', label='var2')
plt.legend()
plt.show()
【问题讨论】:
-
你所说的“损失梯度的一部分”到底是什么意思?由于损失梯度的净效应本质上是通过模型反向传播并改变可训练模型的权重,这是否意味着您在自定义损失函数中的
var1和var2实际上可以从层权重导出(也许,偏见)? -
变量是未知参数,参与创建观察数据。因此,它们是(非常复杂的)损失函数的一部分,并且模型试图在给定这些未知参数的情况下找到 X 和 y 之间的联系,这些未知参数也需要估计。损失函数的一部分,因此是梯度的一部分,如我的愚蠢示例所示。真实的模型更有趣也更有意义。
-
不错。无论如何,只要在训练后立即更新损失函数参数,那么 keras Callback 仍然是规范的方法。如何计算损失函数参数超出了这个问题的范围。只要您知道如何更新闭包变量,就可以使其在 keras Callbacks 中工作,如我的回答所示。
标签: python tensorflow keras loss-function