如何禁用神经网络中部分层的训练?附尝试代码片段
如何禁用神经网络中部分层的训练?
嘿,看来你在搭建Keras模型的时候想冻结部分层、不让它们参与训练对吧?这事儿在迁移学习或者模型微调里太常见了,我给你分享几个实用的方法,结合你的代码来举例说明:
方法1:直接设置层的trainable属性为False
这是最直接的标准操作,你可以在定义层的时候就指定,或者模型定义完成后通过层名修改。
比如在你定义Block 1的时候直接冻结:
def get_model(): weight_decay=0.0 inputs = Input(shape=(INPUT_IMAGE_H, INPUT_IMAGE_W, 3)) # Block 1 - 把这两个卷积层设为不可训练 x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1', kernel_regularizer=l2(weight_decay), trainable=False)(inputs) x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2', kernel_regularizer=l2(weight_decay), trainable=False)(x) x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x) # Block 2 - 保持默认的可训练状态 x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1', kernel_regularizer=l2(weight_decay))(x) # ... 后续层继续按你的需求定义 model = Model(inputs, x) return model
如果是已经定义好的模型,也可以事后修改:
model = get_model() # 通过层名找到要冻结的层,设置trainable为False model.get_layer('block1_conv1').trainable = False model.get_layer('block1_conv2').trainable = False # 重点!修改完trainable属性后,必须重新编译模型,否则设置不会生效 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
划重点:修改trainable之后一定要重新编译模型,不然框架还是会更新这些层的参数!
方法2:批量冻结预训练模型的层
如果你用的是像VGG、ResNet这类预训练模型,批量冻结会更高效:
from tensorflow.keras.applications import VGG16 # 加载预训练模型,不包含顶层分类器 base_model = VGG16(weights='imagenet', include_top=False, input_shape=(INPUT_IMAGE_H, INPUT_IMAGE_W, 3)) # 一键冻结所有预训练层 base_model.trainable = False # 然后添加你自己的顶层网络 x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(10, activation='softmax')(x) model = Model(inputs=base_model.input, outputs=x) model.compile(optimizer='adam', loss='categorical_crossentropy')
要是之后想微调部分预训练层,只需要把对应层的trainable改回True,再重新编译就行。
方法3:自定义训练循环,精细控制梯度更新
如果需要更灵活的控制(比如只冻结层里的部分参数,或者自定义梯度更新逻辑),可以写自定义训练循环:
import tensorflow as tf optimizer = tf.keras.optimizers.Adam() loss_fn = tf.keras.losses.CategoricalCrossentropy() # 筛选出需要训练的参数:排除block1两个卷积层的所有参数 trainable_vars = [var for var in model.trainable_variables if not ('block1_conv1' in var.name or 'block1_conv2' in var.name)] @tf.function def train_step(x, y): with tf.GradientTape() as tape: predictions = model(x, training=True) loss = loss_fn(y, predictions) # 只计算可训练参数的梯度 gradients = tape.gradient(loss, trainable_vars) optimizer.apply_gradients(zip(gradients, trainable_vars)) return loss # 训练循环示例 for epoch in range(10): print(f"Epoch {epoch+1}/10") for x_batch, y_batch in train_dataset: loss = train_step(x_batch, y_batch) print(f"Batch loss: {loss:.4f}", end='\r')
最后给你几个小提醒:
- 像
MaxPooling2D这类池化层本身没有可训练参数,所以不需要冻结,冻结了也没影响。 - 可以用
model.summary()查看模型的可训练/不可训练参数数量,确认冻结是否生效。 - 迁移学习时通常先冻结预训练层训练顶层,再解冻部分层微调,效果会更好。
内容的提问来源于stack exchange,提问作者mrgloom
相关产品推荐
相关产品推荐

