深度学习中如何对模型参数进行分组优化
在深度学习中,不同的参数(如权重和偏置、不同层的参数)可能需要不同的优化策略。常见的做法是对 不同参数组使用不同的学习率、权重衰减(L2正则化)或优化算法,这被称为 参数分组优化(Grouped Parameter Optimization)。
1. 为什么需要分组优化?
-
不同类型的参数可能需要不同的学习率
- 卷积层权重 可能需要较小的学习率以稳定训练。
- 全连接层权重 可能需要较大的学习率以加速收敛。
-
权重和偏置的处理方式不同
- 通常对权重使用权重衰减(L2 正则化),以防止过拟合。
- 偏置项通常不使用权重衰减。
-
不同层次的参数可使用不同优化策略
- 预训练模型的底层参数可以使用较小的学习率,避免破坏已有的特征提取能力。
- 高层参数使用较大的学习率,以便适应新的任务。
2. 如何实现参数分组优化?
在 PyTorch 中,可以使用 param_groups
进行参数分组,然后传递给优化器。
示例 1:基础参数分组
import torch
import torch.nn as nn
import torch.optim as optim
# 定义一个简单的神经网络
class SimpleModel(nn.Module):
def __init__(self):
super(SimpleModel, self).__init__()
self.conv1 = nn.Conv2d(3, 16, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(16, 32, kernel_size=3, padding=1)
self.fc1 = nn.Linear(32 * 8 * 8, 128)
self.fc2 = nn.Linear(128, 10)
def forward(self, x):
x = torch.relu(self.conv1(x))
x = torch.relu(self.conv2(x))
x = x.view(x.size(0), -1) # Flatten
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
model = SimpleModel()
# 定义参数组
param_groups = [
{"params": model.conv1.parameters(), "lr": 0.001}, # 第一层卷积较低学习率
{"params": model.conv2.parameters(), "lr": 0.001}, # 第二层卷积较低学习率
{"params": model.fc1.parameters(), "lr": 0.01}, # 全连接层较高学习率
{"params": model.fc2.parameters(), "lr": 0.01} # 输出层较高学习率
]
# 使用 Adam 优化器
optimizer = optim.Adam(param_groups)
# 打印优化器参数
for i, group in enumerate(optimizer.param_groups):
print(f"Group {i}: Learning Rate = {group['lr']}")
解析:
- 卷积层
conv1
和conv2
使用较小的学习率0.001
,以稳定训练。 - 全连接层
fc1
和fc2
使用较大的学习率0.01
,以加快训练。
示例 2:为权重和偏置项使用不同优化策略
在优化过程中,我们可以 为权重和偏置项设置不同的权重衰减参数(L2 正则化)。
# 过滤出权重和偏置
param_groups = [
{"params": [param for name, param in model.named_parameters() if "bias" not in name], "weight_decay": 1e-4}, # 只对权重使用 L2 正则化
{"params": [param for name, param in model.named_parameters() if "bias" in name], "weight_decay": 0} # 偏置不使用 L2 正则化
]
# 使用 SGD 进行优化
optimizer = optim.SGD(param_groups, lr=0.01, momentum=0.9)
# 打印优化器参数
for i, group in enumerate(optimizer.param_groups):
print(f"Group {i}: Weight Decay = {group['weight_decay']}")
解析:
- 第一组参数(
bias
以外的权重)使用weight_decay=1e-4
进行 L2 正则化,防止过拟合。 - 第二组参数(
bias
)不使用权重衰减,避免影响偏置项的更新。
示例 3:冻结部分层,仅微调部分参数
在使用 预训练模型(如 ResNet、BERT) 时,我们通常 冻结底层特征提取层,仅对高层进行微调。
import torchvision.models as models
# 加载预训练的 ResNet
model = models.resnet18(pretrained=True)
# 冻结所有参数
for param in model.parameters():
param.requires_grad = False
# 替换 ResNet 的最后一层
model.fc = nn.Linear(512, 10)
# 仅优化 `fc` 层
optimizer = optim.Adam(model.fc.parameters(), lr=0.01)
# 打印需要训练的参数
for name, param in model.named_parameters():
if param.requires_grad:
print(f"Trainable: {name}")
解析:
- 所有层参数
requires_grad=False
,避免更新底层权重。 - 仅更新
fc
层,以适应新任务。
示例 4:使用不同优化算法
有时候,我们可能希望 不同的参数组使用不同的优化算法,如:
- CNN 层使用 Adam
- 全连接层使用 SGD
- 部分层使用 RMSprop
# 定义不同优化器
optimizer_cnn = optim.Adam([
{"params": model.conv1.parameters(), "lr": 0.001},
{"params": model.conv2.parameters(), "lr": 0.001}
])
optimizer_fc = optim.SGD([
{"params": model.fc1.parameters(), "lr": 0.01},
{"params": model.fc2.parameters(), "lr": 0.01}
], momentum=0.9)
# 训练时分别更新
for batch in data_loader:
optimizer_cnn.zero_grad()
optimizer_fc.zero_grad()
outputs = model(batch)
loss = loss_fn(outputs, target)
loss.backward()
optimizer_cnn.step()
optimizer_fc.step()
解析:
- CNN 层使用 Adam,适合复杂结构。
- 全连接层使用 SGD,更适合梯度稳定的层。
3. 结论
参数分组优化在深度学习中至关重要,常见应用包括:
- 不同层使用不同学习率(如 CNN 层较低,FC 层较高)。
- 对权重使用 L2 正则化,对偏置不使用。
- 冻结底层,仅微调高层(如 ResNet、BERT 微调)。
- 不同层使用不同优化算法(如 CNN 用 Adam,FC 用 SGD)。
通过 param_groups
,可以灵活地控制不同参数的优化策略,提高训练效率和模型性能。