为什么 Keras Tuners 的固定超参数会产生与静态值不同的结果?

Why do Keras Tuners' Fixed hyperparameters produce different results from static values?

即使我的模型应该有效地使用相同的参数,我的超调结果也大不相同,这取决于我是使用 hp.Fixed(key, value) 还是只使用 value(其中 value 是说,一个整数)。我已经验证每个测试的重复运行都会在 instructions for reproducibility 之后产生相同的结果,并为所有适用的 layers/initializers/etc 设置种子。尽管说明上说它们不是必需的。

使用hp.Fixed(key, value)

使用value

查看所有超参数的 table,似乎 hp.Fixed 甚至什么都没做。正在测试所有超参数。

编辑:无论状态如何,我的自定义超模型的超参数都被 HyperbandTuner 忽略。


这是有问题的代码:

class MyModel(kt.HyperModel):
    def __init__(self, **config):
        self.config = config
        self.seed = config.get('seed')
    
    def build_model(self):
        model = Sequential(name=self.name)
        model.add(LSTM(self.units, name='LSTM'))
        model.add(Dense(1, name='Output', kernel_initializer=GlorotUniform(seed=self.seed)))
        model.compile(loss='mean_squared_error', metrics='mean_squared_error', sample_weight_mode='temporal')
        return model
    
    # If the user has supplied the parameter manually, use hp.Fixed()
    # Otherwise, use the provided hyperparameter (default)
    def _param(self, key, default=None):
        value = self.config.get(key)
        if value is not None:
            return self.hp.Fixed(key, value)
        else:
            return default

    def build(self, hp):
        self.hp = hp
        self.units = self._param('units', hp.Int('units', 1, 200, step=5))
        return self.build_model()

好的,在进行更多挖掘之后我发现(特别是通过 this tutorial 声明超参数的方式)当您编写 hp.[Fixed|Choice|etc.] 时,您会立即声明这些超参数在搜索中的存在 space,无论该代码出现在何处。

将超参数声明视为特征class 方法,而不是 HyperTuner 从模型中获取的常规 Python 对象。

本质上,每个Fixed/Choice/etc。超参数方法同时在 HyperTuner class 后台的某处设置一个全局超参数,同时还返回一个常规变量 (Int/Float/String/Range/List/etc.),这样您仍然可以在 HyperTuner 最终覆盖它之前构建您的模型而不会出错搜索阶段。

我对此感到困惑,因为通常 hpbuild_model() 方法或 kt.HyperModel class 中显示为参数,其中调用被分配给局部变量然后传递给模型声明。


下面是对违规代码的修复:

class MyModel(kt.HyperModel):
    def __init__(self, **config):
        self.config = config
        self.seed = config.get('seed')
    
    def build_model(self):
        model = Sequential(name=self.name)
        model.add(LSTM(self.units, name='LSTM'))
        model.add(Dense(1, name='Output', kernel_initializer=GlorotUniform(seed=self.seed)))
        model.compile(loss='mean_squared_error', metrics='mean_squared_error', sample_weight_mode='temporal')
        return model
    
    # If the user has supplied the parameter manually, use hp.Fixed()
    # Otherwise, use the provided hyperparameter (default)
    def _param(self, key, default=None):
        value = self.config.get(key)
        if value is not None:
            return self.hp.Fixed(key, value)
        else:
            return default()

    def build(self, hp):
        self.hp = hp
        self.units = self._param('units', lambda: hp.Int('units', 1, 200, step=5))
        return self.build_model()