加载模型后修改优化器或学习率出现异常结果的问题排查
解决Keras加载模型后继续训练的优化器问题
我之前也碰到过类似的坑,咱们一步步拆解问题出在哪,再给你对应解决方案:
问题1:加载模型直接继续训练,准确率卡在86%不动
这是因为你加载的模型不仅包含权重,还保存了原优化器的训练状态(比如Adam的一阶矩、二阶矩,或者SGD的动量)。之前训练到86%时,优化器已经进入收敛平台期,再加上你设置了decay=0.02,学习率经过衰减后已经非常小,几乎无法再更新权重,所以继续训练准确率没变化。
问题2:重新编译换优化器后,准确率暴跌到15%-20%
这是Keras的正常机制:重新调用model.compile()时,会完全重置优化器的状态——哪怕你用了和原模型一样的权重,新优化器是从头初始化的(没有之前训练积累的动量、矩信息)。对于Adam这种依赖历史状态的优化器来说,新优化器的更新方向会和已收敛的权重完全不匹配,导致模型性能骤降;就算是SGD,没有之前的动量状态,初始更新也会非常“生硬”,表现很差。
问题3:直接赋值model.optimizer = sgd不生效
这是很多人踩的坑:Keras在第一次编译模型时,会把优化器的更新逻辑(比如梯度计算、权重更新步骤)绑定到模型的训练函数中。之后直接修改model.optimizer属性,只是替换了一个变量,但实际训练时调用的还是编译时生成的旧训练函数,根本不会用到新的优化器实例,所以你调大学习率也没反应。
针对性解决方案
方案1:不想换优化器,只是想调整学习率突破平台
不用重新编译,直接修改现有优化器的学习率参数即可,这样能保留原优化器的训练状态:
from keras import backend as K model = load_model(load_path) # 查看当前学习率 print("Current LR:", K.get_value(model.optimizer.lr)) # 修改学习率(比如调到0.001) K.set_value(model.optimizer.lr, 0.001) # 或者用TensorFlow的assign方法(TensorFlow后端更推荐) model.optimizer.lr.assign(0.001) # 直接继续训练,此时学习率已经更新,会看到准确率/损失的波动 history = model.fit_generator(...)
方案2:必须更换优化器(比如从Adam换SGD)
不能直接重新编译,要分两步走:先保存模型权重,再用新优化器编译新模型后加载权重,最后用小学习率warm-up过渡:
# 1. 加载原有模型,只保存权重(不保存优化器状态) old_model = load_model(load_path) old_model.save_weights("trained_weights.h5") # 2. 重新定义和原模型完全一致的模型结构 def build_your_model(): # 这里复制你原来的模型构建代码,比如: # model = Sequential() # model.add(Dense(...)) # ... return model new_model = build_your_model() # 3. 用新优化器编译模型,加载权重 new_optimizer = optimizers.SGD(lr=0.00001) # 先用极小学习率warm up new_model.compile(optimizer=new_optimizer, loss='categorical_crossentropy', metrics=['accuracy']) new_model.load_weights("trained_weights.h5") # 4. 先warm up训练2-3个epoch,再逐步提高学习率 new_model.fit_generator(train_gen, epochs=3, ...) # 之后可以用回调函数调整学习率,比如: from keras.callbacks import LearningRateScheduler def adjust_lr(epoch): if epoch < 5: return 0.00001 elif epoch < 10: return 0.0001 else: return 0.001 lr_scheduler = LearningRateScheduler(adjust_lr) new_model.fit_generator(train_gen, epochs=20, callbacks=[lr_scheduler], ...)
方案3:自定义训练循环(进阶)
如果你不想重新构建模型,可以手动用Keras后端的函数定义训练步骤,强制使用新优化器:
from keras import backend as K model = load_model(load_path) new_optimizer = optimizers.Adam(lr=0.001) # 定义损失函数和训练步骤 loss_fn = K.categorical_crossentropy inputs = [model.input, model.targets[0], K.learning_phase()] loss = loss_fn(model.output, model.targets[0]) updates = new_optimizer.get_updates(model.trainable_weights, model.constraints, loss) # 构建训练函数 train_fn = K.function(inputs, [loss], updates=updates) # 然后自己写训练循环(示例) for epoch in range(r_epochs): print(f"Epoch {epoch+1}/{r_epochs}") total_loss = 0.0 for step in range(r_steps_per_epoch): x_batch, y_batch = next(train_gen) loss_val = train_fn([x_batch, y_batch, 1])[0] total_loss += loss_val print(f"Train loss: {total_loss/r_steps_per_epoch:.4f}") # 可以自己加验证逻辑
内容的提问来源于stack exchange,提问作者Moondra
相关产品推荐
相关产品推荐

