Schwertlilien
As a recoder: notes and ideas.

2025-10-7

在具体的代码层面如何实现仅更新CLIP中的部分参数?

CLIP-V 局部参数微调与层冻结的代码实施

在代码层面,“局部参数微调 + 部分层冻结” 的核心是 “通过参数的‘requires_grad’属性控制是否更新”,不同 CLIP 架构(CNN/ViT)的实施方式略有差异,以 PyTorch 框架为例,具体步骤如下:

1. 核心逻辑:区分 “冻结层” 与 “微调层” 的参数梯度

PyTorch 中,模型参数的requires_grad属性决定了该参数是否会在反向传播中更新:

  • requires_grad=True:参数会被优化(微调);
  • requires_grad=False:参数固定(冻结),反向传播时不计算梯度。

实施的关键是 “先加载预训练 CLIP 模型,再针对性修改特定层的requires_grad”,而非从头定义模型。

2. 分架构代码示例(以 Hugging Face transformers库的CLIPVisionModel为例)

(1)CNN 类 CLIP(如 ConvNeXt-B/CLIP):微调全部层 + 低学习率

CNN 类 CLIP(如 ConvNeXt、ResNet)的预训练特征提取能力强,微调全部层但需降低学习率(避免破坏预训练权重),代码逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
from transformers import CLIPVisionModel

# 1. 加载预训练CLIP视觉编码器(ConvNeXt-B/CLIP)
clip_v = CLIPVisionModel.from_pretrained("openai/clip-vit-base-patch32") # 示例,实际需对应ConvNeXt版本

# 2. 微调全部层:所有参数requires_grad=True(默认加载后为True,无需额外操作)
# 3. 训练时单独设置CLIP-V的学习率为其他模块的1/100(如PSM模块学习率1e-3,CLIP-V为1e-5)
optimizer = torch.optim.AdamW([
{"params": clip_v.parameters(), "lr": 1e-5}, # CLIP-V低学习率微调
{"params": psm_module.parameters(), "lr": 1e-3} # PSM模块正常学习率
])

(2)ViT 类 CLIP(如 ViT-B/16):仅微调位置编码与注意力投影层

ViT 的 Transformer 块(encoder.layers)是核心语义提取模块,需冻结;仅微调 “位置编码(embeddings.position_embeddings)” 和 “注意力输入投影层(encoder.layers.*.self_attn.q_proj等)”,代码逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from transformers import CLIPVisionModel

# 1. 加载预训练ViT类CLIP视觉编码器
clip_v = CLIPVisionModel.from_pretrained("openai/clip-vit-base-patch32")

# 2. 冻结所有层(先全局设为False)
for param in clip_v.parameters():
param.requires_grad = False

# 3. 解冻“位置编码层”(微调以适配掩码区域的空间信息)
clip_v.embeddings.position_embeddings.requires_grad = True

# 4. 解冻“注意力输入投影层”(所有Transformer层的q_proj、k_proj、v_proj)
for layer in clip_v.encoder.layers:
layer.self_attn.q_proj.requires_grad = True
layer.self_attn.k_proj.requires_grad = True
layer.self_attn.v_proj.requires_grad = True

# 5. 训练时设置解冻层的学习率(如1e-4,高于CNN类CLIP)
optimizer = torch.optim.AdamW([
{"params": clip_v.embeddings.position_embeddings.parameters(), "lr": 1e-4},
{"params": [p for layer in clip_v.encoder.layers for p in [layer.self_attn.q_proj.parameters(),
layer.self_attn.k_proj.parameters(),
layer.self_attn.v_proj.parameters()]], "lr": 1e-4},
{"params": psm_module.parameters(), "lr": 1e-3}
])

3. 工程验证:确保冻结层无梯度更新

训练中可通过torch.nn.utils.clip_grad_norm_或打印梯度值,验证冻结层是否无梯度:

1
2
3
4
# 反向传播后,检查冻结层(如ViT的encoder.layers.0.layer_norm1)的梯度
for name, param in clip_v.named_parameters():
if "encoder.layers.0.layer_norm1" in name:
print(f"{name} grad: {param.grad is None}") # 输出True,说明无梯度(冻结成功)
搜索
匹配结果数:
未搜索到匹配的文章。