更改 Keras 模型变量

Change Keras Model variable

我想逐渐增加 Keras 模型中用于计算损失的系数。变量值基于当前纪元。但是,当我想设置值时,出现以下错误:

float object has no attribute dtype

我的代码:

def warm_up(epoch, logs):
    new_value=  tf.keras.backend.variable(np.array(1.0, dtype=np.float32), dtype=tf.float32)
    tf.keras.backend.set_value(model.variable1, new_value)

callback = tf.keras.callbacks.LambdaCallback(on_epoch_begin=warm_up)

model.fit(..., callbacks = [callback])

如何在训练期间更改自定义 Keras 模型中的变量?我正在使用 Tensorflow 2.2。

回溯:

\Anaconda3\lib\site-packages\tensorflow\python\keras\engine\training.py in _method_wrapper(self, *args, **kwargs)
     64   def _method_wrapper(self, *args, **kwargs):
     65     if not self._in_multi_worker_mode():  # pylint: disable=protected-access
---> 66       return method(self, *args, **kwargs)
     67 
     68     # Running inside `run_distribute_coordinator` already.

~\Anaconda3\lib\site-packages\tensorflow\python\keras\engine\training.py in fit(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, validation_batch_size, validation_freq, max_queue_size, workers, use_multiprocessing)
    836       for epoch, iterator in data_handler.enumerate_epochs():
    837         self.reset_metrics()
--> 838         callbacks.on_epoch_begin(epoch)
    839         with data_handler.catch_stop_iteration():
    840           for step in data_handler.steps():

~\Anaconda3\lib\site-packages\tensorflow\python\keras\callbacks.py in on_epoch_begin(self, epoch, logs)
    347     logs = self._process_logs(logs)
    348     for callback in self.callbacks:
--> 349       callback.on_epoch_begin(epoch, logs)
    350     self._reset_batch_timing()
    351 

c:\Users\..\training.py in warm_up(epoch, logs)
    379 def warm_up(epoch, logs):
    380     test =  tf.keras.backend.variable(np.array(1.0, dtype=np.float32), dtype=tf.float32)
--> 381     tf.keras.backend.set_value(model.variable1, test)
    382 
    383 

~\Anaconda3\lib\site-packages\tensorflow\python\keras\backend.py in set_value(x, value)
   3349           (of the same shape).
   3350   """
-> 3351   value = np.asarray(value, dtype=dtype(x))
   3352   if ops.executing_eagerly_outside_functions():
   3353     x.assign(value)

~\Anaconda3\lib\site-packages\tensorflow\python\keras\backend.py in dtype(x)
   1266 
   1267   """
-> 1268   return x.dtype.base_dtype.name
   1269 
   1270 

AttributeError: 'float' object has no attribute 'dtype'

编辑:我将代码更改为以下内容:

class LossCallback(tf.keras.callbacks.Callback):
    def __init__(self):
        super(LossCallback, self).__init__()
        self.model.beta_x = tf.Variable(1.0, trainable=False, name='weight1', dtype=tf.float32)

    def on_epoch_begin(self, epoch, logs=None):
        tf.keras.backend.set_value(self.model.beta_x, tf.constant(0.5) * epoch)

    def on_epoch_end(self, epoch, logs=None):
        logs = logs or {}
        logs['beta_x'] = tf.keras.backend.get_value(self.model.beta_x)

我仍然在 on_epoch_begin 中遇到错误:'NoneType' object has no attribute 'beta_x'

避免直接编辑变量。 您必须像这样访问 keras 变量

import tensorflow as tf
from tensorflow import keras
import numpy as np

def warm_up(epoch, logs):
    val = keras.backend.get_value(model.optimizer.lr)
    val *= 1.1
    tf.keras.backend.set_value(model.optimizer.lr, val)
    

callback = tf.keras.callbacks.LambdaCallback(on_epoch_begin=warm_up)

model = tf.keras.models.Sequential([
    keras.layers.Dense(10, 'relu'),
    keras.layers.Dense(1, 'sigmoid')
])

model.compile(loss='binary_crossentropy')

X_train = tf.random.uniform((10,10))
y_train = tf.ones((10,))
model.fit(X_train, y_train, 
          callbacks = [callback])

请注意我是如何获取当前值的 val = keras.backend.get_value(model.optimizer.lr)。这是在运行时获取正确值的正确方法。 另外,不要在循环内使用或声明新变量。您可能可以通过阅读和更改旧版本获得 new_value。 此外,请避免在回调中使用除 tensorflow 之外的任何其他库,尤其是当您的回调将经常被调用时。不要使用 numpy,使用 tensorflow。几乎总是有一个 tensorflow optaion 可以满足您的需求。

编辑: 如果你有一些自定义值要更新,你可以使用这样的模式:

class LossCallback(tf.keras.callbacks.Callback):
    def __init__(self):
        super(LossCallback, self).__init__()
        self.someValue = tf.Variable(1.0, trainable=False, name='weight1', dtype=tf.float32)

    def on_epoch_end(self, epoch, logs=None):
        tf.keras.backend.set_value(self.model.loss.someValue, self.someValue * epoch)

或者您仍然可以尝试使用 lambda 回调。
从回调中,您可以访问模型的任何变量。像这样self.model.someVariable。您还可以访问在模型的自定义 __init__ 函数中定义的任何自定义变量,如下所示:

#in model's custom __init__
def __init__(self, someArgs):
    ...
    self.someArg = someArgs
    ...

#in callback's "on_epoch_..." method
    ...
    keras.backend.set_value(self.model.someArg, 42)
    ...

请注意,您不能在回调的 __init__ 函数中使用 self.model,因为当回调的 __init__ 为模型时,模型仍未初始化打电话。

这有帮助吗?

编辑 2: 当我首先初始化我的模型并将其作为额外参数添加到它起作用的回调方法时。所以解决方法如下:

class LossCallback(tf.keras.callbacks.Callback):
    def __init__(self, model):
        super(LossCallback, self).__init__()
        model.beta_x = tf.Variable(1.0, trainable=False, name='weight1', dtype=tf.float32)

    def on_epoch_begin(self, epoch, logs=None):
        tf.keras.backend.set_value(self.model.beta_x, tf.constant(0.5) * epoch)

    def on_epoch_end(self, epoch, logs=None):
        logs = logs or {}
        logs['beta_x'] = tf.keras.backend.get_value(self.model.beta_x)

model = create_model() # initialize custom keras model
callback = LossCallback(model)

model.fit(..., callbacks=[callback])

感谢@tornikeo 的大力帮助!