我编写了一个LSTM模型来预测序列数据。
def get_model(config, num_features, output_size): opt = Adam(learning_rate=get_deep(config, 'hp.learning_rate'), beta_1=get_deep(config, 'hp.beta_1')) inputs = Input(shape=[None, num_features], dtype=tf.float32, ragged=True) layers = LSTM(get_deep(config, 'hp.lstm_neurons'), activation=get_deep(config, 'hp.lstm_activation'))( inputs.to_tensor(), mask=tf.sequence_mask(inputs.row_lengths())) layers = BatchNormalization()(layers) if 'dropout_rate' in config['hp']: layers = Dropout(get_deep(config, 'hp.dropout_rate'))(layers) for layer in get_deep(config, 'hp.dense_layers'): layers = Dense(layer['neurons'], activation=layer['activation'])(layers) layers = BatchNormalization()(layers) if 'dropout_rate' in layer: layers = Dropout(layer['dropout_rate'])(layers) layers = Dense(output_size, activation='sigmoid')(layers) model = Model(inputs, layers) model.compile(loss='mse', optimizer=opt, metrics=['mse']) model.summary() return model
我已经调整了一些层的参数,使用了AWS SageMaker。在验证模型时,我多次运行了一个特定配置的模型。大多数时候结果相似,但有一次运行的效果明显优于其他几次,这让我认为模型的初始状态对于获得最佳性能可能至关重要。
正如这个视频中所建议的,权重初始化可以提供一些性能提升。我在网上搜索并找到了层权重初始化器,但我不确定应该调整哪些范围。
更新:正如一些评论/回答中建议的那样,我使用固定种子来“锁定”模型结果:
# 将`python`内置的伪随机生成器设置为固定值random.seed(seed_value)# 将`numpy`的伪随机生成器设置为固定值np.random.seed(seed_value)# 将`tensorflow`的伪随机生成器设置为固定值tf.random.set_seed(seed_value)
每次新的训练结果都可以复制,但是不同的种子可能会产生比其他种子更好的结果。那么我该如何找到/调整最佳种子呢?
回答:
…这让我认为模型的初始状态对于获得最佳性能可能至关重要……正如这个视频中所建议的,权重初始化可以提供一些性能提升。我在网上搜索并找到了层权重初始化器,但我不确定应该调整哪些范围。
首先,在那个视频中,除了状态或权重初始化器之外,所有其他因素如学习率、调度、优化器、批次大小、损失函数、模型深度等都是你应该尝试调整以找到最佳组合(我们稍后会讨论种子的作用)。通常,我们不需要调整默认的权重或状态初始化器,因为这些是当前最好的;而且通常,这种状态初始化是一个研究问题。
其次,在keras中,Convolution
、Dense
和RNN-GRU/LSTM
的默认权重初始化器是glorot_uniform,也称为Xavier均匀初始化器。默认的偏置初始化器是zeros。如果你查看LSTM
的源代码(在你的情况下),你会发现它们。关于这一点,根据文档
从
[-limit, limit]
范围内的均匀分布中抽取样本,其中limit = sqrt(6 / (fan_in + fan_out))
(fan_in
是权重张量中的输入单元数量,fan_out是输出单元数量)。
现在,你可能已经注意到这个初始化器继承自VarianceScaling;与GlorotUniform相同,GlorotNormal, LecunNormal, LecunUniform, HeNormal, HeUniform等也是继承自它的。对于VarianceScaling,这里列出了支持的参数。例如,从技术上讲,以下两个是相同的。
# 如果你想尝试各种初始化器 - # 使用VarianceScaling传递适当的参数。 # 即。tf.keras.layers.LSTM(..., kernel_initializer=initializer)# 但建议坚持使用glorot_uniform(默认)initializer = tf.keras.initializers.VarianceScaling(scale=1., mode='fan_avg', seed=101, distribution='uniform')print(initializer(shape=(2, 2)))initializer = tf.keras.initializers.GlorotUniform(seed=101)print(initializer(shape=(2, 2)))tf.Tensor([[-1.0027379 1.0746485] [-1.2234 -1.1489409]], shape=(2, 2), dtype=float32)tf.Tensor([[-1.0027379 1.0746485] [-1.2234 -1.1489409]], shape=(2, 2), dtype=float32)
简而言之,你可以使用tf.keras.initializers.VarianceScaling(在页面底部)进行尝试。此外,你可以通过定义可调用函数或通过子类化Initializer
类来创建自己的初始化器。例如:
def conv_kernel_initializer(shape, dtype=None): kernel_height, kernel_width, _, out_filters = shape fan_out = int(kernel_height * kernel_width * out_filters) return tf.random.normal( shape, mean=0.0, stddev=np.sqrt(2.0 / fan_out), dtype=dtype)def dense_kernel_initializer(shape, dtype=None): init_range = 1.0 / np.sqrt(shape[1]) return tf.random.uniform(shape, -init_range, init_range, dtype=dtype)
这里有一篇关于权重初始化的好文章,你可能会喜欢阅读。但同样,最好还是使用默认的初始化器。
第三,对于设置不同的种子值和不同的超参数集等,我最好留下我之前的一个回答这里,主要是第一张图表可能会对你的实验有帮助。我遵循的一个方法是保持我的种子相同(比如前5次实验),然后改变另一个因素并记录结果。经过5次迭代后,我们希望能得到一些最佳组合,并进一步探索。
更新
查找/调整种子。在寻找查找最佳种子的方法之前,必须理解种子不是需要与其他超参数(如学习率、调度器、优化器等)一起调整的超参数。
这里有一个场景,假设你使用种子42随机将数据分为两部分:训练集(70%)和测试集(30%),然后在训练集上训练后,你在模型的测试集上评估并获得了80分。然后你将种子改为101,再次进行相同的操作,但这次你得到了50分。现在,这并不意味着选择种子42更好;但这仅仅意味着你的模型不稳定,很可能在未见过的数据上表现不佳。这实际上是一个众所周知的问题,如果有人随机分割他们的数据集进行训练和测试。为什么会发生这种情况?因为当你随机分割数据时,可能存在类分布的不匹配。请查看以下两个非常相关的讨论: