冻结 base_model 后模型不更新权重,根本原因是冻结操作必须在 compile() 之前完成且需触发 model.build() 或首次 fit() 才固化图结构;冻结后必须重新 compile(),否则 trainable 状态不生效。

冻结 base_model 后训练顶层,为什么模型不更新权重?
常见现象是:明明设了 base_model.trainable = False,又加了新分类层,但训练时 model.trainable_weights 为空,或验证准确率卡死不动。根本原因不是冻结错了,而是冻结操作必须在 compile() 之前完成,且需触发一次 model.build() 或首次 fit() 才真正固化图结构。
- 冻结后务必调用
model.compile(),不能复用冻结前编译过的模型实例 - 若用
tf.keras.applications加载预训练模型(如ResNet50),默认include_top=False,此时要手动接Dense层,别漏掉GlobalAveragePooling2D - 检查
len(model.trainable_weights)是否为 0(冻结后)→ 接上新层后是否 > 0,这是最直接的验证方式
trainable = False 后还能否微调部分中间层?
可以,但顺序很重要:先设 base_model.trainable = False,compile() 训练几轮;再设 base_model.trainable = True,**必须重新 compile()**,否则新 trainable 状态不会生效。更稳妥的做法是只解冻靠后的若干层,避免梯度爆炸。
- 推荐解冻策略:从倒数第 3 个
Conv2D块开始(比如 ResNet 的最后一个conv5_block3_),而不是全放开 - 解冻后建议降低学习率,例如用
Adam(1e-5),否则底层特征提取器容易被破坏 - 注意 BatchNormalization 层行为:设
trainable=True时,BN 的moving_mean/moving_variance会更新;若只想微调权重不更新统计量,应显式设layer.trainable = True后,再把 BN 层的training参数固定为False(需自定义call())
用 tf.data 配合迁移学习时,prefetch 和 cache 放哪?
顺序错会导致内存暴涨或数据增强失效。典型错误是把 cache() 放在 map(data_augment) 之后——这会让增强后的图像被缓存,失去随机性;而放在 map(data_augment) 之前又没意义,因为原始图像未归一化,缓存价值低。
- 正确链路:
dataset.map(preprocess_input).cache().map(data_augment).batch().prefetch() preprocess_input是模型配套的归一化函数(如tf.keras.applications.resnet50.preprocess_input),必须在cache前执行,否则缓存的是原始像素值,后续每次都要重复计算- 小数据集(<5k 样本)可全程
cache();大数据集建议只cache()训练集,验证集跳过,避免爆内存
加载权重后 model.evaluate() 结果异常高,但 fit() 时 loss 不降
这不是过拟合,大概率是预训练模型的预处理和你训练时用的不一致。比如用 MobileNetV2 却没走 tf.keras.applications.mobilenet_v2.preprocess_input,或者用了 ResNet50 却输入了 [0, 1] 归一化的图而非 [-1, 1]。
- 确认
preprocess_input函数和模型严格匹配,不同版本(v1/v2)、不同 backend(TensorFlow/PyTorch 转换)预处理逻辑可能不同 - 检查输入张量 shape 和 dtype:必须是
float32,且 shape 为(None, 224, 224, 3)类(取决于模型),不要用uint8直接喂入 - 冻结训练时,如果验证集准确率远高于训练集(比如 98% vs 60%),大概率是验证数据被意外“泄露”进训练流程(如
cache()范围过大、shuffle关闭)