保存并加载模型优化器状态

Sta*_*ter 6 python machine-learning keras tensorflow

我有一套相当复杂的模型,我正在训练,我正在寻找一种方法来保存和加载模型优化器状态."训练模型"由几个其他"体重模型"的不同组合组成,其中一些具有共同的权重,一些具有取决于训练者的冻结权重等.分享的例子有点过于复杂,但简而言之,我无法使用model.save('model_file.h5'),keras.models.load_model('model_file.h5')停止和开始训练时.

model.load_weights('weight_file.h5')如果训练已经完成,使用可以很好地测试我的模型,但是如果我尝试使用这种方法继续训练模型,那么损失甚至不会回到最后位置.我已经读过这是因为没有使用这种方法保存优化器状态是有意义的.但是,我需要一种方法来保存和加载我的教练模型的优化器的状态.似乎keras曾经拥有过,model.optimizer.get_sate()而且model.optimizer.set_sate()这将完成我所追求的目标,但似乎不再是这种情况了(至少对于Adam优化器而言).当前的Keras还有其他解决方案吗?

Yu-*_*ang 14

您可以从load_modelsave_model函数中提取重要的行.

要保存优化程序状态,请在save_model:

# Save optimizer weights.
symbolic_weights = getattr(model.optimizer, 'weights')
if symbolic_weights:
    optimizer_weights_group = f.create_group('optimizer_weights')
    weight_values = K.batch_get_value(symbolic_weights)
Run Code Online (Sandbox Code Playgroud)

对于加载优化器状态,​​在load_model:

# Set optimizer weights.
if 'optimizer_weights' in f:
    # Build train function (to get weight updates).
    if isinstance(model, Sequential):
        model.model._make_train_function()
    else:
        model._make_train_function()

    # ...

    try:
        model.optimizer.set_weights(optimizer_weight_values)
Run Code Online (Sandbox Code Playgroud)

结合上面的行,这是一个例子:

  1. 首先使模型适合5个时期.
X, y = np.random.rand(100, 50), np.random.randint(2, size=100)
x = Input((50,))
out = Dense(1, activation='sigmoid')(x)
model = Model(x, out)
model.compile(optimizer='adam', loss='binary_crossentropy')
model.fit(X, y, epochs=5)

Epoch 1/5
100/100 [==============================] - 0s 4ms/step - loss: 0.7716
Epoch 2/5
100/100 [==============================] - 0s 64us/step - loss: 0.7678
Epoch 3/5
100/100 [==============================] - 0s 82us/step - loss: 0.7665
Epoch 4/5
100/100 [==============================] - 0s 56us/step - loss: 0.7647
Epoch 5/5
100/100 [==============================] - 0s 76us/step - loss: 0.7638
Run Code Online (Sandbox Code Playgroud)
  1. 现在保存权重和优化器状态.
model.save_weights('weights.h5')
symbolic_weights = getattr(model.optimizer, 'weights')
weight_values = K.batch_get_value(symbolic_weights)
with open('optimizer.pkl', 'wb') as f:
    pickle.dump(weight_values, f)
Run Code Online (Sandbox Code Playgroud)
  1. 在另一个python会话中重建模型,并加载权重.
x = Input((50,))
out = Dense(1, activation='sigmoid')(x)
model = Model(x, out)
model.compile(optimizer='adam', loss='binary_crossentropy')

model.load_weights('weights.h5')
model._make_train_function()
with open('optimizer.pkl', 'rb') as f:
    weight_values = pickle.load(f)
model.optimizer.set_weights(weight_values)
Run Code Online (Sandbox Code Playgroud)
  1. 继续模型培训.
model.fit(X, y, epochs=5)

Epoch 1/5
100/100 [==============================] - 0s 674us/step - loss: 0.7629
Epoch 2/5
100/100 [==============================] - 0s 49us/step - loss: 0.7617
Epoch 3/5
100/100 [==============================] - 0s 49us/step - loss: 0.7611
Epoch 4/5
100/100 [==============================] - 0s 55us/step - loss: 0.7601
Epoch 5/5
100/100 [==============================] - 0s 49us/step - loss: 0.7594
Run Code Online (Sandbox Code Playgroud)

  • 这里的‘K’是什么?`将 keras.backend 导入为 K`? (3认同)
  • @Yu-Yang - 跟进@DvD_95 的评论。我认为 `_make_train_function` 不再存在(至少在 TF2.3 中)。也就是说,有 `model.make_train_function()` (没有下划线)。但是,当我在 Adam 优化器上使用它时,我得到: **ValueError: You Called set_weights(weights) on optimizationr Adam with a length 255, but the optimizationr was Expected 0 Weights.** 我检查了 src 代码,它看起来“set_weights”确实应该起作用。对此有什么想法吗? (3认同)
  • 我相信这似乎奏效了,至少损失没有像以前那样激增。现在它似乎开始比它停止下降的地方高一点,下降得更快。谢谢@Yu-Yang。我最终使用了 save_model 和 load_model 函数,只是删除了权重的保存和加载 (2认同)

Ale*_*ick 10

对于那些不使用model.compile而是执行自动微分以手动应用渐变的人optimizer.apply_gradients,我想我有一个解决方案。

首先,保存优化器权重: np.save(path, optimizer.get_weights())

然后,当您准备好重新加载优化器时,通过调用optimizer.apply_gradients您为其计算梯度的变量大小的张量列表,向新实例化的优化器显示它将更新的权重大小。在设置优化器的权重之后设置模型的权重非常重要,因为即使我们给模型的梯度为零,基于动量的优化器(如 Adam)也会更新模型的权重。

import tensorflow as tf
import numpy as np

model = # instantiate model (functional or subclass of tf.keras.Model)

# Get saved weights
opt_weights = np.load('/path/to/saved/opt/weights.npy', allow_pickle=True)

grad_vars = model.trainable_weights
# This need not be model.trainable_weights; it must be a correctly-ordered list of 
# grad_vars corresponding to how you usually call the optimizer.

optimizer = tf.keras.optimizers.Adam(lrate)

zero_grads = [tf.zeros_like(w) for w in grad_vars]

# Apply gradients which don't do nothing with Adam
optimizer.apply_gradients(zip(zero_grads, grad_vars))

# Set the weights of the optimizer
optimizer.set_weights(opt_weights)

# NOW set the trainable weights of the model
model_weights = np.load('/path/to/saved/model/weights.npy', allow_pickle=True)
model.set_weights(model_weights)
Run Code Online (Sandbox Code Playgroud)

请注意,如果我们apply_gradients在第一次调用之前尝试设置权重,则会引发错误,优化器期望长度为零的权重列表。

  • 这很有帮助,节省了我很多时间进行重新培训,谢谢! (2认同)
  • 顺便说一句,我找到了一个解决方案来避免“apply_gradients”和“zero_grads”计算。解决方案是在“with tf.name_scope(optimizer._name):”和“with tf.init_scope():”中应用“optimizer._create_all_weights(model.trainable_variables)”。解决方案可以在`apply_gradients()`方法的源代码中找到。请参阅第 516-519 行的[源](https://github.com/tensorflow/tensorflow/blob/v2.3.1/tensorflow/python/keras/optimizer_v2/optimizer_v2.py#L735-L771)。 (2认同)