Keras 更改有关历元的自定义损耗参数和NN参数

Keras 更改有关历元的自定义损耗参数和NN参数,keras,parameters,loss-function,Keras,Parameters,Loss Function,我有一个Keras模型,定义方式如下(尽量只保留必要的部分): 我跟在后面 这是使用带有tf后端的Keras上的fit generator()实现的。显然,我在加载模型时会遇到问题,因为温度是共享编码的。 也, 我希望更新损耗函数和模型中有关历元数的温度参数 我如何定义这样的控制信号 我已经把它变成了一个完整的例子,说明了一种方法 您可以为损失函数创建一个类 class TemperatureLossFunction: def __init__(self, temperature):

我有一个Keras模型,定义方式如下(尽量只保留必要的部分):

我跟在后面

这是使用带有tf后端的Keras上的
fit generator()
实现的。显然,我在加载模型时会遇到问题,因为
温度
是共享编码的。 也, 我希望更新损耗函数和模型中有关历元数的
温度
参数


我如何定义这样的控制信号

我已经把它变成了一个完整的例子,说明了一种方法

您可以为损失函数创建一个类

class TemperatureLossFunction:
    def __init__(self, temperature):
        self.temperature = temperature
    def loss_fun(self, y_truth, y_pred):
        return self.temperature*keras.losses.mse(y_truth, y_pred)
    def setTemperature(self, t, session=None):
        if session:
            session.run(self.temperature.assign( t )
        elif tensorflow.get_default_session():
            tensorflow.get_default_session().run(self.temperature.assign( t ))

class TemperatureLossCallback(keras.callbacks.Callback):
    def __init__(self, temp_lf):
        self.temp_lf = temp_lf
    def on_epoch_end(self, epoch, params):
        self.temp_lf.setTemperature(epoch)
我创建了两种方法来处理这个问题,第一种方法创建并保存模型

def init(session):
    global temperature #global for serialization issues
    temperature = tensorflow.Variable(5.0)
    tlo = TemperatureLossFunction(temperature)

    inp = keras.layers.Input((4,4))

    l1 = keras.layers.Lambda( lambda x: temperature*x )
    op = l1(inp)

    m = keras.models.Model(inputs=[inp], outputs=[op])
    m.compile( optimizer = keras.optimizers.SGD(0.01), loss=tlo.loss_fun)

    #make sure the session is the one your using!
    session.run(temperature.initializer)
我运行的第一个测试确保我们正在更改值

    m.evaluate( numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4)) )    
    session.run(temperature.assign(1))
    m.evaluate( numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4)) )
我运行的第二个测试确保我们可以通过回调更改值

    cb = TemperatureLossCallback(tlo)
    def gen():
        for i in range(10):
            yield numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4))
    m.fit_generator(
            gen(), steps_per_epoch=1, epochs=10, callbacks=[cb]
        )
    m.save("junk.h5")
最后,演示如何重新加载文件

def restart(session):
    global temperature
    temperature = tensorflow.Variable(5.0)
    tlo = TemperatureLossFunction(temperature)
    loss_fun = tlo.loss_fun
    m = keras.models.load_model(
            "junk.h5", 
            custom_objects = {"loss_fun":tlo.loss_fun}
    )
    session.run(temperature.initializer)
    m.evaluate( numpy.ones((1, 4, 4)), numpy.zeros((1, 4, 4)) )
    session.run(temperature.assign(1))
    m.evaluate( numpy.ones( (1, 4, 4) ), numpy.zeros( ( 1, 4, 4) ) )
这正是我用来启动程序的完整性代码

import sys    
if __name__=="__main__":
    sess = tensorflow.Session()
    with sess.as_default():
        if "restart" in sys.argv:
            restart(sess)
        else:
            init(sess)
这种方法的一个缺点是,如果运行它,您将看到温度变量不会从模型文件中加载。它接受代码中指定的值

从正面来看,损失函数和层都引用了相同的
变量

我发现保存变量值的一种方法是创建一个新层,并使用该变量作为新层的权重

class VLayer(keras.layers.Layer):
    def __init__(self, *args, **kwargs):
        super().__init__(**kwargs)
    def build(self, input_shape):
        self.v1 = self.add_weight(
                       dtype="float32", 
                       shape = (), 
                       trainable=False, 
                       initializer="zeros"
                   )
    def call(self, x):
        return x*self.v1
    def setValue(self, val):
        self.set_weights( numpy.array([val]) )

现在,加载模型时,将加载权重。不幸的是,我找不到一种方法将重量与负载变量联系起来。因此将有两个变量,一个用于损失函数,一个用于层。不过,这两个参数都可以通过回调进行设置。因此,我觉得这种方法更可靠。

这对我来说很重要。我希望稍后加载模型并使用它进行预测。这会给我带来麻烦吗?@havakok当你加载模型并编译时,它会很好。这意味着,如果你开始训练,然后尝试重新开始训练,会有一些不同。对于预测来说,这很好,因为预测不使用训练/优化器值,它只使用经过训练的权重。那么,你需要一个新的答案吗?不,我只是想给matt一笔赏金,感谢他的帮助。
class VLayer(keras.layers.Layer):
    def __init__(self, *args, **kwargs):
        super().__init__(**kwargs)
    def build(self, input_shape):
        self.v1 = self.add_weight(
                       dtype="float32", 
                       shape = (), 
                       trainable=False, 
                       initializer="zeros"
                   )
    def call(self, x):
        return x*self.v1
    def setValue(self, val):
        self.set_weights( numpy.array([val]) )