ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Adam凭什么比SGD快?手写三组优化器,NumPy跑出10个数量级差距,ML数学基础第8期

Adam凭什么比SGD快?手写三组优化器,NumPy跑出10个数量级差距,ML数学基础第8期

标
前七期我们攒齐了半套装备:第2期注意力是矩阵乘法,第3期softmax,第4期交叉熵,第5期LayerNorm,第6期反向传播,第7期讲学习率。今天把第7期留的尾巴收了:学习率调对之后,优化器本身还能不能再快?

先摆个靶子。Rosenbrock函数,长得像一根香蕉,谷底又细又长,是优化器的经典考场。起点(-1, 1),函数值4.0,目标是走到谷底的(1, 1)。SGD学习率0.001,跑2000步,函数值还剩7.42e-02。能走,但慢。

慢在哪?香蕉谷底两个方向的坡度差了上百倍。学习率按最陡的方向设,到了平缓方向就只能一步步挪。这时候动量(momentum)登场。想法很直观:梯度方向基本一致的时候,把历史梯度累积起来,越走越快;方向来回震荡的时候,正负抵消,自动减速。像推着一个小球滚下山,有惯性。

代码不长,核心就三行:

importnumpyasnpdefrosenbrock(x):return(1-x[0])**2+100*(x[1]-x[0]**2)**2defgrad(x):g0=-2*(1-x[0])-400*x[0]*(x[1]-x[0]**2)g1=200*(x[1]-x[0]**2)returnnp.array([g0,g1])defmomentum(x0,lr=0.001,beta=0.9,steps=2000):x=x0.copy()v=np.zeros(2)# 速度,累积历史梯度for_inrange(steps):v=beta*v+grad(x)x-=lr*vreturnrosenbrock(x)

beta=0.9是惯例,相当于记住最近10步左右的梯度方向。

Adam是两步再往前:动量管"往哪走",再加一个"每步走多大"。它给每个参数单独算学习率:梯度一直很大的方向,步子自动调小;梯度稀稀拉拉的方向,步子自动放大。用梯度平方的滑动平均来估计每个方向的"忙碌程度"。另外有个容易忽略的细节叫偏差修正:刚开始滑动平均都是0,前几步会被低估,除以(1-beta^t)把它修正回来。

defadam(x0,lr=0.01,steps=2000):x=x0.copy()m=np.zeros(2)# 一阶矩:梯度的滑动平均(动量)v=np.zeros(2)# 二阶矩:梯度平方的滑动平均(自适应步长)b1,b2=0.9,0.999fortinrange(1,steps+1):g=grad(x)m=b1*m+(1-b1)*g v=b2*v+(1-b2)*g*g mh=m/(1-b1**t)# 偏差修正vh=v/(1-b2**t)x-=lr*mh/(np.sqrt(vh)+1e-8)returnrosenbrock(x)

环境只要numpy,pip install numpy就行。

四组实验,我机器上真实跑出来的数字,起点都是(-1, 1),都跑2000步:

SGD,学习率0.001:7.42e-02
Momentum,学习率0.001:8.97e-09
Adam,学习率0.001:1.23e-01
Adam,学习率0.01:1.74e-19

看出门道了吗。动量是质变:同样学习率,Momentum比SGD快了7个数量级。Adam在学习率0.01时最猛,1.74e-19,基本贴着谷底。但注意第三行:Adam在学习率0.001时只走到1.23e-01,还不如SGD。这就是今天最大的坑:Adam不是无脑强,它照样吃学习率,学习率没设对,再花哨的自适应也救不了。第7期讲学习率不是白讲的,优化器换得再勤,学习率这一课都躲不掉。

为什么Adam在小学习率下反而拉胯?它的自适应步长是按历史梯度平方来缩放的,Rosenbrock这种狭长山谷里,二阶矩估计会把本就不大的步子压得更小,2000步根本走不到谷底。调大到0.01,一阶矩的动量效果才放出来。所以Adam的"自适应"不是"不用调",是"调起来和SGD不是一个手感"。

第二个坑:Adam快,但不总是最好。小模型、数据干净、调参时间充足的时候,SGD+动量调好了,泛化经常更好。Adam是"开箱即用"的选择,SGD是"愿意花时间调"的选择。面试被问"为什么默认用Adam",别只背"收敛快",把这句也带上。

收藏这篇,下次有人问你Adam和SGD的区别,直接把这四组数字甩出来,比背公式管用。

下期预告:第9期讲正则化——L2、Dropout、早停,三个防止过拟合的手段,NumPy手写看效果。这个系列在专栏《ML数学基础》里连着更新,从矩阵乘法一路写到优化器,关注后专栏里能连着看。

讨论:你训模型时默认用Adam还是SGD?有没有遇到过Adam还不如SGD的时候,最后怎么解决的?

—— Cyrus

返回列表