Keras调优找不到回调

y4ekin9u  于 2023-03-30  发布在  其他
关注(0)|答案(1)|浏览(118)

我使用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],
    )

现在我发现,在hyperband开始使用相当数量的迭代之后,我设置的回调应该开始发挥作用,我得到这个错误:

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.8keras-tuner 1.1.2
我只能在网上找到一个类似问题的地方,但没有提供解决方案:https://issuemode.com/issues/tensorflow/tensorflow/72982126

编辑

1.提供完整的错误消息
1.经过进一步的调试,问题完全来自使用生成器作为.search()的输入。我不知道这是一个问题的原因。使用.fit()进行常规训练没有任何问题
1.增加了数据集生成代码,用于重现性

kx7yvsdv

kx7yvsdv1#

查看错误的源代码,并查看提供的similar error,看起来这个问题不是由于实际的模型回调引起的(tf.keras.callbacks.EarlyStoppingCallback)。错误发生在FuncRegistry类中,这是一个帮助器,它维护了一个唯一标记到注册的python函数的Map,在这两种情况下,看起来都是这样,该令牌(pyfunc_XXX)不Map到函数。当调用_internal_py_func时,函数插入此处,在 Package Python函数时(要作为渴望的Tensorflow操作执行)或在计算渴望函数的梯度时。(FuncRegistry对象)被提供给initialize_py_trampolineinitialize_py_trampoline通过PyBind绑定到C中的InitializePyTrampoline函数,因此标记到函数Map的引用也在C运行时中维护。
在这个级别上,从日志中跟踪错误到C++源代码,它发生在内部类Iterator的析构函数中,GeneratorDatasetOp的字段。析构函数在对象超出范围或显式删除时被调用-这意味着它将在生成器完成其任务时被调用,这听起来可能与错误发生时您所做的观察一致。
总而言之,如果没有数据集就无法进一步探测,那么听起来自定义生成器可能有问题。我建议尝试在没有keras-tuner和相同的生成器实现的情况下执行训练,以确定问题是否与其他链接的观察一致,因为他们没有使用keras-tuner,而是使用自定义生成器。如果错误仍然存在,如果以前的版本(例如,Tensorflow 2.7或更低版本)的生成器也存在同样的问题。如果它一直失败,则可能需要向Tensorflow Github存储库提交实际问题,因为它实际上可能是一个核心错误,需要进一步研究。
此外,如果你不需要使用生成器(比如,数据可以放入内存),我建议尝试直接提供数据集(使用numpy数组列表或numpy数组调用fit,而不是提供生成器函数),因为该路径不会触及当前失败的DatasetGenerator代码,这应该不会影响你的超参数搜索。

更新

感谢您提供的额外信息,包括复制生成器函数的代码。我能够在CPU上重现Python 3.7/Tensorflow 2.8/keras-tuner 1.1.2中的问题。如果您检查_funcs(全局注册表中的字段,用于维护对函数的弱引用的标记字典),它实际上是空的。进一步检查后,看起来每次开始新的试用时,_funcs都会被清除并重新填充,如果keras-tuner每次都创建新的图形(模型),则这是一致的(尽管始终使用相同的注册表FuncRegistry)。
如果省略了EarlyStopping回调,则不会发生错误,因此您说错误与回调有关是正确的。此外,错误似乎是不确定的,因为每次运行的尝试和出现的时期都不同。
随着错误原因的缩小,另一个人经历了the same issue,他们的观察结果是错误的原因与在回调中显式设置min_delta参数有关,正如您所做的那样,其他keras-tuner示例都没有这样做(例如;在文档中的这个example和这个example中,它们只设置了monitor和/或patience)。
EarlyStopping回调中设置min_delta的影响,默认设置为0,可以在这里看到。具体来说,当min_delta设置为某个非零值时,_is_improvement计算为True的频率会降低:

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_opnp.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

self._is_improvement的计算频率较低时,patience标准(self.wait >= self.patience)将更频繁地得到满足,因为self.wait的重置频率较低(因为默认情况下self.baseline为None):

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中需要提交的一个bug,我用这个响应中的所有细节做了here。为了在此期间继续进行,如果min_delta条件不是必要的,我建议从EarlyStopping中删除该参数并再次运行脚本,看看是否仍然遇到这个问题。

更新2

谢谢你的额外信息。如果不使用生成器,我能够重现成功的运行,而且看起来other case I referenced也使用了与EarlyStopping一起使用的生成器,并提供了min_delta

经过进一步检查,在注册表中找不到的函数是finalize_py_func,因为在清除_funcs之前,每个导致错误Map到finalize_py_func的标记中。它 Package 了一个python函数作为Tensorflow op使用。finalize_py_func被定义并作为Tensorflow op返回的函数finalize_fn在构建生成器时提供,在这里可以看到。查看生成器中finalize函数的文档,它说“在此数据集上的C++迭代器被销毁之前,将在init_func`的结果上调用的TensorFlow函数。”

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

我已经修改了github issue以包含该细节。看起来错误仍然存在于Tensorflow 2.7中,但如果您降级到Tensorflow 2.6(和Keras 2.6),则不会发生错误。如果降级是可能的,这可能是继续进行直到问题得到解决的最佳选择。

相关问题