如何在TensorFlow与Keras迁移学习中防止过拟合?
解决TensorFlow Hub预训练层微调过拟合的标准方案
遇到这种封装好的预训练层没法直接修改内部结构的情况,业内有一套成熟的处理思路,我帮你拆解下最实用的几个方案,按优先级来:
1. 分层解冻(渐进式微调)—— 最推荐的核心方案
直接全开trainable=True过拟合,本质是预训练的通用权重被你的小数据集带偏了。正确的做法是先冻结预训练层,只训练你自己加的顶层,等顶层收敛后,再逐步解冻预训练层的后几层(靠近输出的层,这些层的特征更偏向任务无关到任务相关的过渡),同时用极小的学习率训练,避免破坏预训练的通用特征。
举个代码例子:
# 第一步:冻结预训练层,先训练自定义顶层 hub_layer = hub.KerasLayer("你的预训练模型地址", trainable=False) model = tf.keras.Sequential([ hub_layer, tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid') ]) model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy']) # 先训顶层,用正常学习率 model.fit(train_ds, validation_data=val_ds, epochs=10) # 第二步:渐进式解冻,只让预训练层的最后5层可训练(数量按需调整) hub_layer.trainable = True # 遍历预训练层的内部子层,冻结前面的大部分 for layer in hub_layer.layers[:-5]: layer.trainable = False # 用极小的学习率继续训练,比如比之前小10-100倍 model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5), loss='binary_crossentropy', metrics=['accuracy']) # 接着之前的epoch继续训 model.fit(train_ds, validation_data=val_ds, epochs=30, initial_epoch=10)
2. 给模型加全局正则化—— 无需修改预训练层内部
你说的对,没法给每个hub子层加kernel_regularizer,但可以给所有可训练权重统一加L2正则化,在自定义训练循环里手动计算正则化损失就行,不管权重来自哪里:
loss_fn = tf.keras.losses.BinaryCrossentropy(from_logits=True) optimizer = tf.keras.optimizers.Adam(learning_rate=1e-5) l2_lambda = 1e-4 # 正则化强度,按需调 @tf.function def train_step(x, y): with tf.GradientTape() as tape: logits = model(x, training=True) # 基础损失 base_loss = loss_fn(y, logits) # 收集所有可训练权重的L2损失,统一乘正则化系数 l2_loss = tf.add_n([tf.nn.l2_loss(w) for w in model.trainable_weights]) * l2_lambda total_loss = base_loss + l2_loss gradients = tape.gradient(total_loss, model.trainable_weights) optimizer.apply_gradients(zip(gradients, model.trainable_weights)) return total_loss # 然后用这个train_step循环训练就行 for epoch in range(epochs): for x_batch, y_batch in train_ds: loss = train_step(x_batch, y_batch) # 验证逻辑...
3. 在预训练层外部加正则化层—— 简单直接的补充
虽然没法改hub层内部,但可以在它的输出后面加Dropout、BatchNormalization这类正则化层,同样能有效缓解过拟合:
model = tf.keras.Sequential([ hub_layer, tf.keras.layers.Dropout(0.3), # 给预训练特征加Dropout tf.keras.layers.BatchNormalization(), # 稳定特征分布 tf.keras.layers.Dense(128, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(1e-4)), tf.keras.layers.Dropout(0.5), tf.keras.layers.Dense(1, activation='sigmoid') ])
关于FTRL优化器的使用
TF2里FTRL确实支持内置的L2正则化,但它的参数调起来和Adam不一样,尤其是学习率——FTRL适合用稍大一点的学习率(比如1e-3到1e-2),同时正则化强度要和数据集匹配。如果之前用不好,试试调整这两个参数:
optimizer = tf.keras.optimizers.Ftrl( learning_rate=0.01, l2_regularization_strength=1e-4, learning_rate_power=-0.5 # 这个参数控制学习率衰减,默认值就行 )
不过要注意,FTRL更适合稀疏特征场景(比如文本的one-hot特征),如果你的数据集是图像这类稠密特征,还是Adam加全局L2的组合效果更稳定。
必加的辅助手段:早停(Early Stopping)
不管用哪个方案,都一定要加早停回调,一旦验证集损失不再下降就停止训练,直接用最优的权重:
early_stopping = tf.keras.callbacks.EarlyStopping( monitor='val_loss', patience=5, # 5个epoch没提升就停 restore_best_weights=True # 自动恢复到最优权重 ) model.fit(train_ds, validation_data=val_ds, epochs=50, callbacks=[early_stopping])
内容的提问来源于stack exchange,提问作者Lukáš Lánský
相关产品推荐
相关产品推荐

