Tensorflow 创建keras回调以在培训期间保存每个批次的模型预测和目标

Tensorflow 创建keras回调以在培训期间保存每个批次的模型预测和目标,tensorflow,callback,keras,Tensorflow,Callback,Keras,我正在Keras(tensorflow后端)中构建一个简单的序列模型。在培训期间,我想检查各个培训批次和模型预测。因此,我试图创建一个自定义的回调,它保存每个训练批的模型预测和目标。但是,该模型不是使用当前批次进行预测,而是使用整个训练数据 如何仅将当前培训批交给回调 如何访问回调保存在self.prethis和self.targets中的批和目标 我目前的版本如下: callback\u list=[prediction\u history((self.x\u train,self.y\u t

我正在Keras(tensorflow后端)中构建一个简单的序列模型。在培训期间,我想检查各个培训批次和模型预测。因此,我试图创建一个自定义的
回调
,它保存每个训练批的模型预测和目标。但是,该模型不是使用当前批次进行预测,而是使用整个训练数据

如何仅将当前培训批交给
回调

如何访问
回调
保存在self.prethis和self.targets中的批和目标

我目前的版本如下:

callback\u list=[prediction\u history((self.x\u train,self.y\u train))]
self.model.fit(self.x_train,self.y_train,batch_size=self.batch_size,epochs=self.n_epochs,validation_data=(self.x_val,self.y_val,callbacks=callback_list)
类历史(keras.callbacks.Callback):
定义初始(自身、列车数据):
self.train\u data=列车数据
self.prethis=[]
self.targets=[]
批处理结束时的def(self、epoch、logs={}):
x\u列车,y\u列车=自列车数据
self.targets.append(y_列)
预测=自我模型预测(x_列车)
self.prethis.append(预测)
tf.logging.info(“预测形状:{}”.format(预测形状))
tf.logging.info(“目标形状:{}”.format(y_train.shape))

注意:此答案已过时,仅适用于TF1。检查@bers以获取在TF2上测试的解决方案


模型编译后,
y\u true
的占位符张量位于
模型中。targets
y\u pred
位于
模型中。输出

要在每个批次保存这些占位符的值,可以:

  • 首先将这些张量的值复制到变量中
  • 在批处理端的
    中计算这些变量,并存储结果数组
  • 现在,步骤1有点复杂,因为您必须添加一个
    tf。将
    op分配给培训功能
    model.train\u功能
    。使用当前的Keras API,可以通过在构造训练函数时向
    K.function()
    提供
    获取
    参数来实现

    模型中.\u make\u train\u function()
    中有一行:

    self.train\u功能=K功能(输入,
    [self.total_loss]+self.metrics_张量,
    更新=更新,
    name='train\u函数',
    **自身功能(kwargs)
    
    获取包含
    tf.assign
    操作的
    参数可以通过
    模型提供。_function\u kwargs
    (仅在Keras 2.1.0之后有效)

    例如:

    来自keras.layers
    从keras.models导入顺序
    从keras.callbacks导入回调
    从keras导入后端为K
    导入tensorflow作为tf
    将numpy作为np导入
    类CollectOutputTarget(回调):
    定义初始化(自):
    超级(CollectOutputTarget,self)。\uuuu init\uuuuu()
    self.targets=[]收集y#个真实批次
    self.outputs=[]#收集y#u pred批
    #这两个变量的形状将根据批次形状而变化
    #要处理“最后一批”,请指定'validate\u shape=False`
    self.var_y_true=tf.Variable(0.,validate_shape=False)
    self.var_y_pred=tf.Variable(0.,validate_shape=False)
    批处理端上的def(自身、批处理、日志=无):
    #计算变量并将其保存到列表中
    self.targets.append(K.eval(self.var\u y\u true))
    self.outputs.append(K.eval(self.var_y_pred))
    #建立一个简单的模型
    #必须首先编译要准备的模型目标和模型输出
    模型=顺序([密集(5,输入_形状=(10,)]))
    compile(loss='mse',optimizer='adam')
    #初始化变量和`tf.assign`ops
    cbk=收集输出目标()
    fetches=[tf.assign(cbk.var\u y\u true,model.targets[0],validate\u shape=False),
    tf.assign(cbk.var\u y\u pred,model.outputs[0],validate\u shape=False)]
    模型._function_kwargs={'fetches':fetches}使用'model._function_kwargs`如果使用'model'而不是'Sequential'`
    #拟合模型并检查结果
    X=np.random.rand(10,10)
    Y=np.rand.rand(10,5)
    model.fit(X,Y,批处理大小=8,回调=[cbk])
    
    除非样本数量可以除以批次大小,否则最终批次的大小将不同于其他批次。因此,在这种情况下不能使用
    K.variable()
    K.update()
    。您必须使用
    tf.Variable(…,validate\u shape=False)
    tf.assign(…,validate\u shape=False)


    要验证保存的数组的正确性,可以在
    training.py
    中添加一行以打印无序索引数组:

    if shuffle==“batch”:
    索引数组=\批量\洗牌(索引数组,批量大小)
    elif shuffle:
    np.random.shuffle(索引数组)
    打印('索引数组:',repr(索引数组))#添加此行
    批次=\制造\批次(数量\序列\样本,批次\大小)
    
    在装配过程中,应打印出无序索引数组:

    Epoch 1/1 Index array: array([8, 9, 3, 5, 4, 7, 1, 0, 6, 2]) 10/10 [==============================] - 0s 23ms/step - loss: 0.5670
    如您所见,
    cbk.targets中有两个批次(一个“完整批次”大小为8,最后一个批次大小为2),行顺序与
    Y[index\u array]
    更新:请参阅TF>=2.2

    @Yu-Yang的解决方案的一个问题是它依赖于
    模型。_function\u kwargs
    ,这不能保证工作,因为它不是API的一部分。特别是,在具有急切执行的TF2中,会话KWARG似乎根本不被接受,或者由于急切模式而抢先运行

    因此,下面是我在
    tensorflow==2.1.0
    上测试的解决方案。诀窍是替换
    fetches"""Demonstrate access to Keras symbolic tensors in a (tf.)keras.Callback."""
    
    import numpy as np
    import tensorflow as tf
    
    use_tf_keras = True
    if use_tf_keras:
        from tensorflow import keras
        from tensorflow.keras import backend as K
    
        tf.config.experimental_run_functions_eagerly(False)
        compile_kwargs = {"run_eagerly": False, "experimental_run_tf_function": False}
    
    else:
        import keras
        from keras import backend as K
    
        compile_kwargs = {}
    
    
    in_shape = (2,)
    out_shape = (1,)
    batch_size = 3
    n_samples = 7
    
    
    class CollectKerasSymbolicTensorsCallback(keras.callbacks.Callback):
        """Collect Keras symbolic tensors."""
    
        def __init__(self):
            """Initialize intermediate variables for batches and lists."""
            super().__init__()
    
            # Collect batches here
            self.inputs = []
            self.targets = []
            self.outputs = []
    
            # # For a pure Keras solution, we need to know the shapes beforehand;
            # # in particular, batch_size must divide n_samples:
            # self.input = K.variable(np.empty((batch_size, *in_shape)))
            # self.target = K.variable(np.empty((batch_size, *out_shape)))
            # self.output = K.variable(np.empty((batch_size, *out_shape)))
    
            # If the shape of these variables will change (e.g., last batch), initialize
            # arbitrarily and specify `shape=tf.TensorShape(None)`:
            self.input = tf.Variable(0.0, shape=tf.TensorShape(None))
            self.target = tf.Variable(0.0, shape=tf.TensorShape(None))
            self.output = tf.Variable(0.0, shape=tf.TensorShape(None))
    
        def on_batch_end(self, batch, logs=None):
            """Evaluate the variables and save them into lists."""
            self.inputs.append(K.eval(self.input))
            self.targets.append(K.eval(self.target))
            self.outputs.append(K.eval(self.output))
    
        def on_train_end(self, logs=None):
            """Print all variables."""
            print("Inputs: ", *self.inputs)
            print("Targets: ", *self.targets)
            print("Outputs: ", *self.outputs)
    
    
    @tf.function
    def assign_keras_symbolic_tensors_metric(_foo, _bar):
        """
        Return the assignment operations as a metric to have them evaluated by Keras.
    
        This replaces `fetches` from the TF1/non-eager-execution solution.
        """
        # Collect assignments as list of (dest, src)
        assignments = (
            (callback.input, model.inputs[0]),
            (callback.target, model._targets[0] if use_tf_keras else model.targets[0]),
            (callback.output, model.outputs[0]),
        )
        for (dest, src) in assignments:
            dest.assign(src)
    
        return 0
    
    
    callback = CollectKerasSymbolicTensorsCallback()
    metrics = [assign_keras_symbolic_tensors_metric]
    
    # Example model
    model = keras.Sequential([keras.layers.Dense(out_shape[0], input_shape=in_shape)])
    model.compile(loss="mse", optimizer="adam", metrics=metrics, **compile_kwargs)
    
    # Example data
    X = np.random.rand(n_samples, *in_shape)
    Y = np.random.rand(n_samples, *out_shape)
    
    model.fit(X, Y, batch_size=batch_size, callbacks=[callback])
    print("X: ", X)
    print("Y: ", Y)