如何在CNN模型中冻结卷积层内部分参数而非整层全部参数?
CNN单层内部分参数冻结的实现方法
不需要整层冻结的时候,核心逻辑非常直接:深度学习框架只会对开启了梯度计算的参数执行更新,你只要给不需要更新的那部分参数关掉梯度,再让优化器只更新开了梯度的参数就行,完全不需要改训练循环的逻辑。
就拿你说的「10个卷积filter只更新5个、冻结剩余5个」的场景,用最常用的PyTorch实现的话步骤非常少:
- 先定位到目标卷积层的权重张量:卷积层权重的维度顺序是
[filter总数, 输入通道数, 卷积核高, 卷积核宽],第一维正好对应你说的10个filter。 - 先确保整层权重的梯度计算是打开的,再把你要冻结的那5个filter对应的权重切片,单独把这部分的
requires_grad设为False,如果卷积层带偏置参数,对应位置的偏置也要同步关掉梯度。 - 初始化优化器的时候,不要直接传入全模型参数,先筛选一遍所有
requires_grad=True的参数传给优化器,这步是避免冻结失效的关键。
具体可运行的示例代码如下:
import torch import torch.nn as nn class DemoCNN(nn.Module): def __init__(self): super().__init__() # 目标卷积层:共10个3x3的filter,输入通道为3 self.target_conv = nn.Conv2d(in_channels=3, out_channels=10, kernel_size=3) self.cls_head = nn.Linear(10, 2) def forward(self, x): x = self.target_conv(x) x = x.mean(dim=[2,3]) # 全局平均池化 return self.cls_head(x) model = DemoCNN() # ========== 核心冻结逻辑 ========== # 冻结第5-9号共5个filter,只更新0-4号的5个filter # 先打开整层权重的梯度,避免之前的配置干扰 model.target_conv.weight.requires_grad = True # 给后5个filter的权重关掉梯度 model.target_conv.weight[5:, :, :, :].requires_grad = False # 同步处理对应位置的偏置 if model.target_conv.bias is not None: model.target_conv.bias.requires_grad = True model.target_conv.bias[5:].requires_grad = False # 优化器只传入需要更新的参数 trainable_params = filter(lambda p: p.requires_grad, model.parameters()) optimizer = torch.optim.SGD(trainable_params, lr=1e-3, momentum=0.9) # 后续正常写训练循环即可,反向传播时冻结的5个filter不会计算梯度,参数值会保持初始状态完全不变
几个容易踩的坑提前说:
- 不要上来就给整层设
requires_grad=False,一旦整层梯度被关掉,你再单独开切片部分的梯度是不生效的。- 梯度开关的设置一定要放在优化器初始化之前,要是先初始化优化器再改
requires_grad,优化器的参数列表里还是会包含冻结的参数,会导致冻结失效。- 如果用TensorFlow/Keras实现逻辑完全一致,只要在前向传播时把要冻结的权重切片用
tf.stop_gradient()包裹,就能阻止对应部分的参数更新。
内容的提问来源于stack exchange,提问作者ZeroorOne
相关产品推荐
相关产品推荐

