超参数基本功:学习率、批大小、迭代次数
模型训练的本质是啥?说白了,就是在损失函数的曲面上找一个最低点。但你手里拿的不是地图,而是一堆旋钮——学习率、批大小、迭代次数。这三个玩意儿调不好,模型要么跑偏,要么原地踏步。我入行那几年踩过的坑,十有八九都跟它们有关。
学习率:步子大了容易扯着蛋
学习率控制着模型参数每一次更新的步长。你想想看,如果一个盲人在下山,步子太大可能直接滚下悬崖,步子太小可能走到天黑都到不了山脚。
我个人习惯把学习率想象成「勇气值」:
- 太大:梯度更新跳过最优点,损失值震荡甚至发散
- 太小:收敛慢如蜗牛,还容易陷在局部最优里出不来
- 恰好:稳定快速逼近全局最优
下面是学习率对训练曲线的影响,我整理成了一张表方便你看:
| 学习率设置 | 训练损失曲线形状 | 常见后果 |
|---|---|---|
| 过高(> 0.1 典型情况) | 剧烈震荡或不下降 | 模型不收敛,损失值来回跳 |
| 适度(0.01 ~ 0.001) | 平滑下降,曲线顺滑 | 稳定收敛,效果最佳 |
| 过低(< 0.0001) | 下降极为缓慢,几乎平线 | 训练时间暴增,易过拟合 |
批大小:不是越大越好
批大小(batch size)决定了每次更新参数时你看多少样本。不是越多越好,也不是越少越好,讲究的是平衡。
小批量(比如 32 或 64)噪音大,但能帮你跳出局部陷阱。大批量(比如 256 或 512)梯度估计更准,但容易一头扎进尖锐的局部极小值。
来看一下常见的实际效果对比:
| 批大小 | 梯度精确度 | 梯度更新稳定性 | 泛化能力 |
|---|---|---|---|
| 小(16~64) | 低(噪音大) | 低(震荡明显) | 通常更好 |
| 中(128~256) | 中 | 中 | 适中 |
| 大(512~4096) | 高 | 高(稳定) | 可能变差 |
我在项目中遇到过一件事。用 ResNet-50 训练图像分类,batch size 从 32 提到 512,训练时间确实缩短了,但最终测试准确率掉了 3 个点。后来加了学习率 warmup 才补回来。
迭代次数:多练不等于好
迭代次数就是你让模型在训练集上跑了多少遍。太少,欠学习;太多,过拟合。
我见过一些初学者直接把 epochs 设为 500,然后去看剧——回来发现模型把训练数据背得滚瓜烂熟,一到测试集就拉胯。这叫一个尴尬。
怎么判断该跑多久?看验证集损失:
- 训练损失下降,验证损失也下降:继续练,还没到头
- 训练损失下降,验证损失开始上升:停!过拟合已经开始了
- 两者都持平不动:可能是学习率太小,或者模型容量不够
一般我会用早停机制自动结束训练。配置长这样:
from torch.optim.lr_scheduler import ReduceLROnPlateau
import numpy as np
class EarlyStopping:
def __init__(self, patience=5, min_delta=0.001):
self.patience = patience
self.min_delta = min_delta
self.best_loss = np.inf
self.counter = 0
def check(self, val_loss):
if val_loss < self.best_loss - self.min_delta:
self.best_loss = val_loss
self.counter = 0
return True
else:
self.counter += 1
if self.counter >= self.patience:
return False # 停止训练
return True
用这个函数包裹你的训练循环,一旦验证损失连续 5 个 epoch 没有明显下降,果断停。省时间,省资源,效果还好。
三者如何协同工作
这三个参数不是孤立存在的。它们的关系我画了张图,你看完就清楚了:
你看,三个节点各自带着关键属性,最后汇聚到「收敛效果」——真正的落地指标。我建议的调优路径是先稳住学习率,再调批大小,最后用迭代次数做优化收尾。
- 学习率先选 0.001,观察损失曲线走势
- 批大小从 64 起步,显存允许可以试 128 或 256
- 迭代次数配合早停,不要硬跑固定轮数
- 如果损失震荡,降学习率或提批大小;如果收敛慢,升学习率或减少批大小
嗯,这三个参数掌握好,训练就从玄学变成了科学。剩下的事情就是慢慢打磨细节了。