Keras 调整找不到回调

2024-04-02

我正在使用 keras-tuner 来获得适合我的模型的最佳超参数集。 我可以为随机数据集重现我的问题:

def generate_data(n_windows, n_timesteps):
    feature_vector_list = []
    label_list = []
    for i in range(10):
        x = tf.random.normal((n_windows, n_timesteps))
        feature_vector = [x]
        choices = [np.array([1, 0]), np.array([0, 1]),
                   np.array([0, 0]), np.array([1,1])]
        labels = np.array([random.choice(choices) for i in range(n_windows)])
        feature_vector_list.append(feature_vector)
        label_list.append(labels)
    return feature_vector_list, label_list


def custom_generator(feat_vector_list, label_list):
    assert len(feat_vector_list) == len(label_list), \
        "Number of feature vectors inconsistent with the number of labels"
    counter = 0
    while True:
        feat_vec = feat_vector_list[counter]
        list_labels = label_list[counter]
        counter = (counter + 1) % len(feat_vector_list)
        yield feat_vec, list_labels

这是模型:

def model_builder(hp):

    n_timesteps, n_features, n_outputs = 60, 1, 2

    hp_units = hp.Int("units", min_value=50, max_value=500, step=50)
    hp_filters = hp.Int("filters", 4, 32, step=4, default=8)
    hp_kernel_size = hp.Int("kernel_size", 3, 50, step=1)
    hp_pool_size = hp.Int("pool_size", 2, 8, step=1)
    hp_dropout = hp.Float("dropout", 0.1, 0.5, step=0.1)

    input1 = Input(shape=(n_timesteps, n_features))
    conv1 = Conv1D(filters=hp_filters,
                   kernel_size=hp_kernel_size,
                   activation='relu')(input1)
    drop1 = Dropout(hp_dropout)(conv1)
    if hp.Choice("pooling", ["max", "avg"]) == "max":
        pool1 = MaxPooling1D(pool_size=hp_pool_size)(drop1)
    else:
        pool1 = AveragePooling1D(pool_size=hp_pool_size)(drop1)
    flatten1 = Flatten()(pool1)
    # hidden layers
    dense1 = Dense(hp_units, activation='relu')(flatten1)
    outputs = Dense(n_outputs, activation='softmax')(dense1)
    model = Model(inputs=[input1, input2], outputs=outputs)
    model.compile(loss='categorical_crossentropy',
                  optimizer=tf.keras.optimizers.Adam(learning_rate=hp.Float("learning_rate",
                                                                            0.01,
                                                                            0.1,
                                                                            step=0.2)),
                  metrics=['accuracy'])
    return model

这是训练脚本:

if __name__ == '__main__':
    x_train, y_train = generate_data(350, 60)
    x_val, y_val = generate_data(80, 60)
    training_generator = custom_generator(x_train, y_train)
    validation_generator = custom_generator(x_val, y_val)
    tuner = kt.Hyperband(
        model_builder,
        objective="val_accuracy",
        max_epochs=70,
        factor=3,
        directory="Results",
        project_name="cnn_tunning"
    )
    stop_early = tf.keras.callbacks.EarlyStopping(monitor='val_loss',
                                                  patience=5,
                                                  min_delta=0.002)

    tuner.search(
        training_generator,
        steps_per_epoch=N_WINDOWS,
        validation_data=validation_generator,
        validation_steps=75,
        callbacks=[stop_early],
    )

现在我发现,在超带开始使用相当数量的迭代之后,我设置的回调应该发挥作用,我收到此错误:

W tensorflow/core/framework/op_kernel.cc:1733] INVALID_ARGUMENT: ValueError: Could not find callback with key=pyfunc_530 in the registry.
Traceback (most recent call last):

  File "/home/diogomota/.cache/pypoetry/virtualenvs/WUAle-Z1-py3.7/lib/python3.7/site-packages/tensorflow/python/ops/script_ops.py", line 259, in __call__
    raise ValueError(f"Could not find callback with key={token} in the "

ValueError: Could not find callback with key=pyfunc_530 in the registry.


W tensorflow/core/kernels/data/generator_dataset_op.cc:107] Error occurred when finalizing GeneratorDataset iterator: INVALID_ARGUMENT: ValueError: Could not find callback with key=pyfunc_530 in the registry.
Traceback (most recent call last):

  File "/home/diogomota/.cache/pypoetry/virtualenvs/WUAle-Z1-py3.7/lib/python3.7/site-packages/tensorflow/python/ops/script_ops.py", line 259, in __call__
    raise ValueError(f"Could not find callback with key={token} in the "

ValueError: Could not find callback with key=pyfunc_530 in the registry.

然而,它只是进行下一次试验,所以我不确定发生了什么,有人可以解释为什么它找不到回调吗?

我在用着tensorflow 2.8 and keras-tuner 1.1.2

我在网上只找到一个有类似问题的地方,但没有提供解决方案:https://issuemode.com/issues/tensorflow/tensorflow/72982126 https://issuemode.com/issues/tensorflow/tensorflow/72982126

EDIT:

  1. 提供完整的错误消息
  2. 经过进一步调试,问题完全来自于使用生成器作为输入.search()。我不知道这个问题的原因。定期训练使用.fit()工作没有任何问题
  3. 添加了数据集生成代码以提高可重复性

看着源代码 https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/python/ops/script_ops.py#L259的错误,并审查类似的错误 https://issuemode.com/issues/tensorflow/tensorflow/72982126提供的,看起来这个问题不是由于实际模型回调引起的(tf.keras.callbacks.EarlyStoppingCallback)。错误发生在FuncRegistry类,它是一个帮助器,维护唯一标记到注册的 python 函数的映射,在这两种情况下,标记 (pyfunc_XXX) 不映射到函数。函数已插入here https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/python/ops/script_ops.py#L348 when _internal_py_func在包装 Python 函数(作为热切 Tensorflow 操作执行)或计算热切函数的梯度时被调用。功能代币的全局注册表(FuncRegistry对象)提供给initialize_py_trampoline,这与InitializePyTrampoline通过 PyBind 在 C++ 中运行函数,因此令牌对函数映射的引用也在 C++ 运行时中维护。

在该级别上,将错误追踪到C++源代码 https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/core/kernels/data/generator_dataset_op.cc#L108从日志来看,它发生在内部类的析构函数中Iterator,一个领域GeneratorDatasetOp。当对象超出范围或显式删除时,将调用析构函数 - 这意味着当生成器完成其任务时,将调用析构函数,这听起来可能与您在错误发生时所做的观察一致。

总之,如果没有数据集就无法进一步探究,听起来自定义生成器可能存在问题。我建议尝试在没有keras-tuner和相同的生成器实现,以确定问题是否与链接的其他观察结果一致,因为他们没有使用keras-tuner但他们使用的是定制生成器。如果错误仍然存​​在,则还值得评估以前的版本(例如 Tensorflow 2.7 或更低版本)是否与生成器存在相同的问题。如果始终失败,则可能需要向 Tensorflow Github 存储库提交实际问题,因为它实际上可能是一个需要进一步探索的核心错误。

另外,如果您不需要使用生成器(例如,数据可以放入内存中),我建议尝试直接提供数据集(调用fit使用 numpy 数组列表或 numpy 数组而不是提供生成器函数),因为该路径不会触及DatasetGenerator当前失败的代码,并且不会影响您的超参数搜索。

Update

感谢您提供附加信息并包括复制生成器函数的代码。我能够在 CPU 上的 Python 3.7/Tensorflow 2.8/keras-tuner 1.1.2 中重现该问题。如果您检查_funcs(全局注册表中的字段维护对函数的弱引用的标记字典),它实际上是空的。经过进一步检查,看起来每次新的试验开始时,_funcs被清除并重新填充,如果 keras-tuner 每次都创建一个新的图形(模型),这是一致的(尽管相同的注册表FuncRegistry贯穿全文)。

如果出现以下情况,则不会发生该错误EarlyStopping回调被省略,所以你说错误与回调相关是正确的。该错误似乎也是不确定的,因为每次运行的试验和发生的时期都不同。

随着错误原因的缩小,另一个人经历了同样的问题 https://www.reddit.com/r/tensorflow/comments/scd0d6/strange_behavior_of_min_delta_in_earlystopping/,他们的观察结果是与显式设置相关的错误的原因min_delta回调中的参数,正如您所做的那样,没有其他keras-tuner示例确实如此(例如;在此example https://keras.io/guides/keras_tuner/distributed_tuning/和这个example https://www.tensorflow.org/tutorials/keras/keras_tuner从文档来看,他们只有monitor and/or patience set).

设置的影响min_delta in the EarlyStopping可以看到callback,默认设置为0here https://github.com/keras-team/keras/blob/v2.8.0/keras/callbacks.py#L1744-L1891。具体来说,_is_improvement当以下情况时,可以较少地评估为 Truemin_delta设置为某个非零值:

    if self._is_improvement(current, self.best):
      self.best = current
      self.best_epoch = epoch
      if self.restore_best_weights:
        self.best_weights = self.model.get_weights()
      # Only restart wait if we beat both the baseline and our previous best.
      if self.baseline is None or self._is_improvement(current, self.baseline):
        self.wait = 0

  def _is_improvement(self, monitor_value, reference_value):
    return self.monitor_op(monitor_value - self.min_delta, reference_value)

请注意,就您而言,self.monitor_op is np.less,因为您正在监控的指标是val_loss:

      if (self.monitor.endswith('acc') or self.monitor.endswith('accuracy') or
          self.monitor.endswith('auc')):
        self.monitor_op = np.greater
      else:
        self.monitor_op = np.less

When self._is_improvement评估频率较低,patience标准(self.wait >= self.patience)会更频繁地遇到,因为self.wait重置频率会降低(如self.baseline默认为无):

if self.wait >= self.patience and epoch > 0:
      self.stopped_epoch = epoch
      self.model.stop_training = True
      if self.restore_best_weights and self.best_weights is not None:
        if self.verbose > 0:
          io_utils.print_msg(
              'Restoring model weights from the end of the best epoch: '
              f'{self.best_epoch + 1}.')
        self.model.set_weights(self.best_weights)

随着范围缩小,这似乎与模型更频繁地停止训练有关,并且当以下情况时,图中对操作的引用不再存在:keras-tuner正在进行试验。

简而言之,这似乎是一个错误keras-tuner需要提交,我就是这么做的here https://github.com/keras-team/keras-tuner/issues/688以及此回复中的所有详细信息。出于同时进行的目的,如果min_delta标准不是必需的,我建议从EarlyStopping并再次运行脚本以查看问题是否仍然存在。

Update 2

感谢您提供额外信息。如果不使用生成器,我能够重现成功的运行,并且它看起来也像我参考的其他案例 https://www.reddit.com/r/tensorflow/comments/scd0d6/strange_behavior_of_min_delta_in_earlystopping/还结合使用了发电机EarlyStopping with a min_delta提供。

经过进一步检查,在注册表中找不到的函数是finalize_py_func,就像在每个导致该错误映射到的标记中一样finalize_py_func before _funcs已清除。finalize_py_func is the 内部函数 https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/python/data/ops/dataset_ops.py#L1100包裹着script_ops.numpy_function, which 包装一个用作 Tensorflow op 的 python 函数 https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/python/ops/script_ops.py#L684。该函数在哪里finalize_py_func被定义并返回为 Tensorflow op,finalize_fn,在构造生成器时提供,可以看出here https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/python/data/ops/dataset_ops.py#L1120。查看生成器中finalize函数的文档here https://github.com/tensorflow/tensorflow/blob/r2.8/tensorflow/python/data/ops/dataset_ops.py#L4555,它说“一个 TensorFlow 函数,将在该数据集上的 C++ 迭代器被销毁之前立即根据 init_func` 的结果调用。”。

总的来说,错误与生成器有关,而不是 min_delta 参数。设置时min_delta加快错误发生的速度,即使min_delta被省略,如果patience降低到足以强制提前停止回调更频繁地触发。使用你的例子,如果你设置patience为 1 并删除min_delta,错误很快就会出现。

我已经修改了github问题 https://github.com/keras-team/keras-tuner/issues/688包括该细节。看起来该错误在 Tensorflow 2.7 中仍然存在,但如果降级到 Tensorflow 2.6(和 Keras 2.6),则不会出现该错误。如果可以降级,这可能是继续进行操作直至问题得到解决的最佳选择。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Keras 调整找不到回调 的相关文章

随机推荐