训练一个神经网络本质上就是在干一件事找一组参数让损失函数最小。而找最小值这件事人类绕了几十年才打磨出一套好用的工具。这套工具里最核心的一条主线就是今天要讲的——优化器Optimizer的进化从朴素的梯度下降GD到随机梯度下降SGD再到带动量的、自适应的最后到集大成的 Adam。看懂这条进化链你就看懂了一个道理每一个优化器的诞生都是为了治上一个优化器的病。第一性原理下山就沿着最陡的方向走想象你半夜被困在一座山上四周漆黑你想下山到最低点。你唯一能感知的是脚下这一小块地面往哪个方向倾斜。最朴素的策略是每一步都朝着当前最陡的下坡方向迈一步。这个方向在数学上就是梯度Gradient的反方向——梯度指向函数增长最快的方向那反着走就是下降最快。于是有了最原始的梯度下降Gradient Descentθ ← θ − η ∇ L ( θ ) \theta \leftarrow \theta - \eta \nabla L(\theta)θ←θ−η∇L(θ)θ \thetaθ是参数η \etaη是学习率步子迈多大∇ L ( θ ) \nabla L(\theta)∇L(θ)是损失函数在θ \thetaθ处的梯度。这套东西没错理论上很美。但它一落地就露馅了——每次算梯度要把整个数据集都过一遍。数据几百万条你迈一步之前得先把几百万条数据全算完。太慢了。SGD步子快了代价是打摆子有人想了个偷懒的招别用全量数据算梯度了每次随机抽一小批甚至一条数据用它的梯度近似全量梯度。这就是随机梯度下降SGDStochastic Gradient Descent。名字里的随机说的就是抽样本是随机的。好处立竿见影步子快多了海量数据也能训。可代价也随之而来——因为每次用的只是一小撮样本这撮样本算出来的梯度跟真实全量梯度是有偏差的而且每次还都不一样。于是参数更新起来就像个喝醉了的人下山大方向对但一路东倒西歪、来回震荡。画在纸上全量梯度下降的路径是一条顺滑的弧线而 SGD 的路径是一条锯齿状的、左右乱抖的线。震荡本身还不是最要命的。最要命的是下面这个场景。病态的地形一个方向陡一个方向平真实世界的损失函数地形往往是畸形的——不同方向的陡峭程度天差地别。想象一个狭长的峡谷横着方向短轴非常陡纵着方向长轴非常平。要下到谷底正确走法是沿着长轴大步往下走。可 SGD 用的是同一个学习率η \etaη去处理所有方向步子太小 → 沿着平缓的长轴爬得像蜗牛慢得要死步子太大 → 在陡峭的短轴上来回弹跳就是下不去甚至直接弹飞。一个学习率伺候不了有的方向陡、有的方向平的地形。这就是优化里著名的病态条件数问题也是 SGD 最深的痛点。从这往后所有的优化器本质上都在回答同一个问题怎么让不同方向用不同的、合适的步子第一服药动量Momentum——记住方向别来回抖针对震荡第一个药方是动量Momentum。它的灵感特别朴素滚下山的球是有惯性的。你这一脚往左、下一脚往右如果只盯着眼前这一脚就永远在左右横跳。可如果你记住之前的方向把历史的方向累积起来那些左右抖动就会互相抵消而一直往下的方向会越滚越快。写成公式就是这样v vv是累积下来的速度v ← β v η ∇ L ( θ ) v \leftarrow \beta v \eta \nabla L(\theta)v←βvη∇L(θ)θ ← θ − v \theta \leftarrow \theta - vθ←θ−vβ \betaβ通常取 0.9意思是我九成信昨天的方向一成信今天的新梯度。这样一来左右横跳因为方向相反、互相抵消一路向下的方向因为始终一致、不断叠加。路径从锯齿变成了顺滑的加速下坡。动量治好了震荡但没治好一个学习率伺候所有方向。第二服药自适应学习率——每个参数各配一个步子对症下药得先知道症在哪。既然不同方向的陡峭程度不同那就别用一个全局学习率让每个参数都拥有自己的学习率。思路是这样的陡的方向梯度经常很大、变化剧烈→ 步子该小一点别弹飞平的方向梯度一直很小→ 步子该大一点别爬不动。怎么判断一个方向陡不陡看它梯度的平方累积量。梯度大的方向平方累积就大梯度小的方向平方累积就小。于是有了AdaGrad每个参数θ i \theta_iθi的分母都除上它自己历史梯度平方和的平方根θ i ← θ i − η G i ϵ ∇ L ( θ i ) \theta_i \leftarrow \theta_i - \frac{\eta}{\sqrt{G_i \epsilon}} \nabla L(\theta_i)θi←θi−Giϵη∇L(θi)G i G_iGi是这个参数所有历史梯度的平方和。陡的方向G i G_iGi大步子自动变小平的方向G i G_iGi小步子自动变大。每个参数都有一双合脚的鞋。但 AdaGrad 有个新毛病G i G_iGi是从训练开始一直累加、永不衰减的越累越大分母越来越大步子就越来越小最后学习率衰减到接近 0模型学不动了。于是有了RMSProp的修补不搞全部历史累加改成指数移动平均让老的梯度平方慢慢被遗忘只重点关注最近一段时间的梯度G i ← β G i ( 1 − β ) ∇ L ( θ i ) 2 G_i \leftarrow \beta G_i (1 - \beta) \nabla L(\theta_i)^2Gi←βGi(1−β)∇L(θi)2这样既保住了陡的方向步子小、平的方向步子大又不会让学习率一路衰减到死。Adam把两服药合在一起到这里两味药都齐了各治各的病动量治震荡让方向更稳、更顺自适应学习率RMSProp治病态地形让每个方向有合适的步子。那把它们合在一起不就是答案吗这就是 AdamAdaptive Moment Estimation自适应矩估计。名字里的矩是概率论里的统计量——一阶矩就是均值二阶矩就是平方的均值。Adam 同时维护了两个东西一阶矩m mm梯度的指数移动平均相当于方向动量那味药二阶矩v vv梯度平方的指数移动平均相当于尺度自适应那味药。更新公式如下β 1 0.9 \beta_10.9β10.9β 2 0.999 \beta_20.999β20.999ϵ 10 − 8 \epsilon10^{-8}ϵ10−8m ← β 1 m ( 1 − β 1 ) g m \leftarrow \beta_1 m (1 - \beta_1) gm←β1m(1−β1)gv ← β 2 v ( 1 − β 2 ) g 2 v \leftarrow \beta_2 v (1 - \beta_2) g^2v←β2v(1−β2)g2m ^ m 1 − β 1 t , v ^ v 1 − β 2 t \hat{m} \frac{m}{1 - \beta_1^t}, \quad \hat{v} \frac{v}{1 - \beta_2^t}m^1−β1tm,v^1−β2tvθ ← θ − η m ^ v ^ ϵ \theta \leftarrow \theta - \eta \frac{\hat{m}}{\sqrt{\hat{v}} \epsilon}θ←θ−ηv^ϵm^多出来的m ^ \hat{m}m^、v ^ \hat{v}v^那两步叫偏差校正因为m mm、v vv一开始都从 0 起步训练头几步会严重偏小除个( 1 − β t ) (1 - \beta^t)(1−βt)把它拉回正常量级。t tt是第几步。把公式翻译成人话Adam 干的就一句话用历史方向稳住方向动量再用历史梯度的平方给每个参数分配合适的步子自适应方向又稳、步子又合脚。为什么 Adam 成了默认选择因为它把两个最要命的坑都填了对学习率不那么敏感你不必绞尽脑汁调那个全局学习率它自己会给每个参数分鞋码收敛快、震荡小动量让它方向顺自适应让它步子合适。当然它不是万能的。有研究表明 Adam 在泛化能力上有时不如精心调参的 SGD 动量因为自适应的步子可能在最后阶段不够克制所以现在也有人用 AdamW、LAMB 这些变体甚至先用 Adam 快速收敛、再切 SGD 精细打磨的混合打法。但作为默认起步的优化器Adam 的地位至今没有被撼动。一个能亲手验证的小例子讲一百遍不如算一遍。下面这段代码用三种优化器去最小化同一个峡谷函数f ( x , y ) 0.1 x 2 10 y 2 f(x, y) 0.1 x^2 10 y^2f(x,y)0.1x210y2——它正是一个方向平x、一个方向陡y的病态地形。你可以把三种优化器的收敛速度拿来对比importnumpyasnpdeff(p):x,ypreturn0.1*x**210*y**2# x 方向平缓y 方向陡峭defgrad(p):x,ypreturnnp.array([0.2*x,20*y])# 梯度y 方向是 x 方向的 100 倍deftrain(opt,steps200,lr0.05):pnp.array([10.0,1.0])# 从 (10, 1) 出发mnp.zeros(2);vnp.zeros(2)fortinrange(1,steps1):ggrad(p)ifoptsgd:pp-lr*gelifoptmomentum:m0.9*mlr*g pp-melifoptadam:m0.9*m0.1*g v0.999*v0.001*g**2mhm/(1-0.9**t)vhv/(1-0.999**t)pp-lr*mh/(np.sqrt(vh)1e-8)returnp,f(p)foroptin[sgd,momentum,adam]:p,losstrain(opt)print(f{opt:8s}- 参数{np.round(p,4)}, 损失{loss:.4f})跑一遍你会发现SGD 在陡峭的 y 方向上来回震荡、收敛极慢动量明显顺了很多Adam 几乎是最快冲到最优解附近的那个。这就是一个学习率伺候不了所有方向和分鞋码之间的差别肉眼可见。结语每一次进化都是给上一个填坑回头看这条进化链特别清爽梯度下降慢 →SGD用随机抽样提速SGD 震荡 →动量用惯性稳住方向一个学习率搞不定陡峭不一的地形 →AdaGrad / RMSProp给每个参数分鞋码学习率过早衰减到 0 →RMSProp用指数平均遗忘两味药合体 →Adam。没有哪个优化器是凭空冒出来的每一个都是发现了前一个的坑然后把坑填上。这跟所有工程技术一样真正的好方案往往不是一开始就设计出来的而是被一个个具体问题逼出来的。理解了这条线你背的不是一堆公式而是一部填坑史。想真正吃透梯度、矩、概率这些优化背后的数学光会用库不够。推荐 B站【408实验室】的《机器学习数学基础》把微积分、概率、线代这三块底子补瓷实——优化器、损失函数、反向传播全会变得通透起来。
阅读完成 · 觉得有帮助?