14
模型调优、正则化与迁移学习
Hyperparameter Tuning & Transfer Learning
跑通 baseline 只是开始,把指标再涨 2 个点靠的是调参和技巧。
超参数搜索:网格 / 随机 / 贝叶斯
| 方法 | 说明 |
|---|---|
| GridSearchCV | 穷举,参数少(3×3×3=27 组合)用。 |
| RandomizedSearchCV | 随机采样,参数空间大时比网格高效。 |
| Optuna(贝叶斯) | 根据历史结果智能选下一组,最少实验次数找最优。当前主流。 |
import optuna
def objective(trial):
params = {
"n_estimators": trial.suggest_int("n_estimators", 100, 500),
"max_depth": trial.suggest_int("max_depth", 3, 10),
"learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3, log=True),
}
model = XGBRegressor(**params)
score = cross_val_score(model, X, y, cv=5, scoring="neg_mean_squared_error").mean()
return score
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=50)
print("最优参数:", study.best_params)
正则化全家桶
| 方法 | 原理 |
|---|---|
| L1 权重衰减 | 损失 + α·|w|,稀疏化。 |
| L2 权重衰减 | 损失 + α·w²,让参数小。AdamW 的 weight_decay 参数。 |
| Dropout | 训练随机关神经元,相当于多模型集成。 |
| 早停 | 验证 loss 不降就停。 |
| 数据增强 | 图像翻转/裁剪/NLP 同义词替换。 |
| 标签平滑 | one-hot 改成 0.9/0.05,防过度自信。 |
迁移学习
from torchvision import models
model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
# 冻结特征提取部分
for p in model.parameters():
p.requires_grad = False
# 只换最后一层
model.fc = nn.Linear(model.fc.in_features, 5) # 5 类
# 先训 fc,再解冻前面几层用更小学习率微调
学习曲线:诊断偏差方差
from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt
train_sizes, train_scores, val_scores = learning_curve(
model, X_train, y_train, cv=5,
train_sizes=np.linspace(0.1, 1.0, 10),
scoring="accuracy", n_jobs=-1
)
train_mean = train_scores.mean(axis=1)
val_mean = val_scores.mean(axis=1)
plt.plot(train_sizes, train_mean, label="训练")
plt.plot(train_sizes, val_mean, label="验证")
plt.xlabel("训练样本数"); plt.ylabel("准确率")
plt.legend(); plt.title("学习曲线")
plt.show()
# 两条线距离大 = 过拟合(高方差)
# 两条线都低 = 欠拟合(高偏差)
# 两条线接近且高 = 刚好
迁移学习代码模板
from torchvision import models
import torch.nn as nn
# 第 1 步:加载预训练模型
model = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
# 第 2 步:冻结所有层
for param in model.parameters():
param.requires_grad = False
# 第 3 步:替换最后一层(你的任务可能是 5 类)
num_ftrs = model.fc.in_features
model.fc = nn.Linear(num_ftrs, 5)
# 第 4 步:只训最后一层(小学习率)
opt = optim.Adam(model.fc.parameters(), lr=1e-3)
# 训练几轮后,如果效果还不够,解冻前面几层用更小学习率微调
for param in model.layer4.parameters():
param.requires_grad = True
opt = optim.Adam(model.parameters(), lr=1e-5)
# 差异化学习率:顶层大、底层小
学习率怎么找:LR Finder
# 快速找最优学习率:从很小开始,每步乘 1.1,画 loss vs lr 曲线
# 选 loss 下降最快那段的拐点值
from torch_lr_finder import LRFinder
lr_finder = LRFinder(model, opt, criterion, device=device)
lr_finder.range_test(train_loader, end_lr=10, num_iter=100)
lr_finder.plot()
# 图上 loss 刚开始下降的拐点就是好的学习率(一般 1e-3 ~ 1e-2)
本章面试题
面试 · 调优
Q1. 学习率太大/太小分别会怎样?
查看答案
太大 loss 震荡甚至发散(变 NaN);太小收敛慢、卡局部最优。一般 1e-3 起步,配合 warmup 和余弦退火。
Q2. batch size 大小影响?
查看答案
大 batch 梯度准、快,但泛化差、内存爆;小 batch 有噪声,反而正则化、泛化好。常用 32/64/128。
Q3. 迁移学习为什么有效?
查看答案
底层特征(边缘、纹理)在大数据上学好可迁移,顶层特征再微调适配新任务。