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

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

如何仅将当前的培训批次移交给“ Callback

以及如何访问Callback保存在self.predhis和self.targets中的批次和目标?

我当前的版本如下所示:

callback_list = [prediction_history((self.x_train, self.y_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)

class prediction_history(keras.callbacks.Callback):
    def __init__(self, train_data):
        self.train_data = train_data
        self.predhis = []
        self.targets = []

    def on_batch_end(self, epoch, logs={}):
        x_train, y_train = self.train_data
        self.targets.append(y_train)
        prediction = self.model.predict(x_train)
        self.predhis.append(prediction)
        tf.logging.info("Prediction shape: {}".format(prediction.shape))
        tf.logging.info("Targets shape: {}".format(y_train.shape))
参考资料:
Stack Overflow
收藏
评论
共 2 个回答
高赞 时间 活跃

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


模型编译后, y_true的占位符张量在model.targetsy_predmodel.outputs

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

  1. 首先将这些张量的值复制到变量中。
  2. on_batch_end评估这些变量,并存储结果数组。

现在涉及到第1步,因为您必须将tf.assign op添加到训练函数model.train_function 。使用当前Keras API,这可以通过提供一种来实现fetches参数K.function()当训练功能构造。

model._make_train_function() ,有一行:

self.train_function = K.function(inputs,
                                 [self.total_loss] + self.metrics_tensors,
                                 updates=updates,
                                 name='train_function',
                                 **self._function_kwargs)

fetches包含参数tf.assign可以通过提供OPS model._function_kwargs (Keras 2.1.0后仅适用)。

举个例子:

from keras.layers import Dense
from keras.models import Sequential
from keras.callbacks import Callback
from keras import backend as K
import tensorflow as tf
import numpy as np

class CollectOutputAndTarget(Callback):
    def __init__(self):
        super(CollectOutputAndTarget, self).__init__()
        self.targets = []  # collect y_true batches
        self.outputs = []  # collect y_pred batches

        # the shape of these 2 variables will change according to batch shape
        # to handle the "last batch", specify `validate_shape=False`
        self.var_y_true = tf.Variable(0., validate_shape=False)
        self.var_y_pred = tf.Variable(0., validate_shape=False)

    def on_batch_end(self, batch, logs=None):
        # evaluate the variables and save them into lists
        self.targets.append(K.eval(self.var_y_true))
        self.outputs.append(K.eval(self.var_y_pred))

# build a simple model
# have to compile first for model.targets and model.outputs to be prepared
model = Sequential([Dense(5, input_shape=(10,))])
model.compile(loss='mse', optimizer='adam')

# initialize the variables and the `tf.assign` ops
cbk = CollectOutputAndTarget()
fetches = [tf.assign(cbk.var_y_true, model.targets[0], validate_shape=False),
           tf.assign(cbk.var_y_pred, model.outputs[0], validate_shape=False)]
model._function_kwargs = {'fetches': fetches}  # use `model._function_kwargs` if using `Model` instead of `Sequential`

# fit the model and check results
X = np.random.rand(10, 10)
Y = np.random.rand(10, 5)
model.fit(X, Y, batch_size=8, callbacks=[cbk])

除非可以将样品数量除以批次大小,否则最终批次的大小将与其他批次不同。因此,在这种情况下,不能使用K.variable()K.update() 。您必须改为使用tf.Variable(..., validate_shape=False)tf.assign(..., validate_shape=False)


要验证保存的数组的正确性,您可以在training.py添加一行以打印出经过改组的索引数组:

if shuffle == 'batch':
    index_array = _batch_shuffle(index_array, batch_size)
elif shuffle:
    np.random.shuffle(index_array)

print('Index array:', repr(index_array))  # Add this line

batches = _make_batches(num_train_samples, batch_size)

改组后的索引数组应在拟合期间打印出来:

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是否与Y[index_array]相同:

index_array = np.array([8, 9, 3, 5, 4, 7, 1, 0, 6, 2])
print(Y[index_array])
[[ 0.75325592  0.64857277  0.1926653   0.7642865   0.38901153]
 [ 0.77567689  0.13573623  0.4902501   0.42897559  0.55825652]
 [ 0.33760938  0.68195038  0.12303088  0.83509441  0.20991668]
 [ 0.98367778  0.61325065  0.28973401  0.28734073  0.93399794]
 [ 0.26097574  0.88219054  0.87951941  0.64887846  0.41996446]
 [ 0.97794604  0.91307569  0.93816428  0.2125808   0.94381495]
 [ 0.74813435  0.08036688  0.38094272  0.83178364  0.16713736]
 [ 0.52609421  0.39218962  0.21022047  0.58569125  0.08012982]
 [ 0.61276627  0.20679494  0.24124858  0.01262245  0.0994412 ]
 [ 0.6026137   0.25620512  0.7398164   0.52558182  0.09955769]]

print(cbk.targets)
[array([[ 0.7532559 ,  0.64857274,  0.19266529,  0.76428652,  0.38901153],
        [ 0.77567691,  0.13573623,  0.49025011,  0.42897558,  0.55825651],
        [ 0.33760938,  0.68195039,  0.12303089,  0.83509439,  0.20991668],
        [ 0.9836778 ,  0.61325067,  0.28973401,  0.28734073,  0.93399793],
        [ 0.26097575,  0.88219053,  0.8795194 ,  0.64887846,  0.41996446],
        [ 0.97794604,  0.91307569,  0.93816429,  0.2125808 ,  0.94381493],
        [ 0.74813437,  0.08036689,  0.38094273,  0.83178365,  0.16713737],
        [ 0.5260942 ,  0.39218962,  0.21022047,  0.58569127,  0.08012982]], dtype=float32),
 array([[ 0.61276627,  0.20679495,  0.24124858,  0.01262245,  0.0994412 ],
        [ 0.60261369,  0.25620511,  0.73981643,  0.52558184,  0.09955769]], dtype=float32)]

如您所见, cbk.targets有两个批次(一个“完整批次”的大小为8,最后一个批次的大小为2),并且行顺序与Y[index_array]相同。

收藏
评论

@ Yu-Yang解决方案的一个问题是,它依赖于model._function_kwargs ,由于它不是API的一部分,因此不能保证能正常工作。特别是,在渴望执行的TF2中,会话渴望似乎根本不被接受,或者由于渴望模式而抢先运行。

因此,这是我的解决方案在tensorflow==2.1.0上进行了测试。诀窍是用Keras量度代替fetches ,在训练过程中根据fetches进行分配操作。

如果批量大小除以样品数量,这甚至可以实现仅Keras的解决方案;否则,在初始化具有None形状的TensorFlow变量时必须采用另一种技巧,类似于早期解决方案中的validate_shape=False (比较https://github.com/tensorflow/tensorflow/issues/35667 )。

重要的是, tf.keras行为与keras不同(有时只是忽略赋值,或将变量视为Keras符号张量),因此此更新的解决方案同时照顾了这两种实现( tensorflow==2.1.0 Keras==2.3.1tensorflow==2.1.0 )。

"""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)
收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号