AdaFactor
从Adam优化器谈起
设t为迭代步数, αt为当前步学习率, L(θ)是损失函数, θ是待优化参数, ϵ是防止溢出的小正数.
Adam优化器的更新过程如下:
⎩⎨⎧gt=∇θL(θt)mt=β1mt−1+(1−β1)gtvt=β2vt−1+(1−β2)gt2m^t=mt/(1−β1t)v^t=vt/(1−β2t)θt=θt−1−αtm^t/v^t+ϵ 作为目前最常用的优化器, 有一个缺点是占用的显存较大. 要省显存, 就首先得知道显存花在哪里的. 首先最后计算得到的梯度是占用显存的, 且这部分是任何优化器都无法节省的.
除此之外, Adam优化器使用了一阶梯度m和二阶梯度v, 且在每一步使用滑动平均计算, 需要进行缓存, 这两部分也要占用缓存, 且各自占用的大小同上面的梯度一致.
AdaFactor节省显存的思路
抛弃动量
Adam性能优秀, 很重要的一个点是每一个参数都有自适应学习率, 从上面的公式中也可以看出:
θt=θt−1−αtm^t/v^t+ϵ
从上式中可以看出, 每个参数的自适应学习率为αt/v^t+ϵ, 即通过SGD+二阶动量来实现的.
因此作为节省缓存的第一步, 考虑直接抛弃一阶动量m, 这样显存的占用直接节省了1/3.
低秩分解
然后继续尝试压缩二阶动量v的大小. AdaFactor使用到了低秩分解.
Adam中每个参数都会有各自独立的学习率, 但SGD中所有的参数共用一个学习率, 且SGD在很多任务或数据集中也能取得不错的效果, 带来的一个思路是精调每一个参数自己的学习率不是特别重要, 因此启发我们将v^t换一种参数更少的近似可能也就足够了. 因此使用低秩分解来实现.
对于m×n大小的矩阵C, 希望找到大小为m×k的矩阵A和大小为k×n的矩阵B, 使得:
AB≈C
使用一个比较小的k值, 这样矩阵A和B中的参数之和会远小于原来C中参数的数量, 且仍能取得近似的效果, 这就是上面说的不再精调每个参数的学习率, 让参数之间共享部分信息.
AdaFactor中取k=1, 将显存节省到了极致, 即寻找{ai}i=1m和{bj}j=1n, 使得:
aibj≈ci,j
为了达到近似的效果, 需要一个距离度量标准来进行约束, 容易想到欧式距离∑i,j(aibj−ci,j)2, 但这样ai,bj没有解析解, 且在优化过程中ci,j, 即对应于更新中的二阶梯度v^t应当是非负的, 但通过上面的目标函数优化得到的ci,j无法保证非负.
因此AdaFactor使用了新的度量标准, 广义KL散度, 形式为:
l=∑i,jci,jlogaibjci,j−ci,j+aibj
这个度量标准来自不等式xlogx≥x−1(∀x>0), 当且仅当x=1时等号成立. 将x=p/q(p,q>0)带入到不等式当中, 然后两端乘以q, 则有:
plogqp−p+q≥0
当且仅当p=q时, 等号成立. 将p替换成ci,j, q替换成aibj, 并且对所有的分量进行求和, 就得到了上面的广义KL散度的公式. 由于有取最小值的条件, 在将ai, bj, ci,j带入后, 刚好有解析解:
ai=j∑ci,j,bj=i,j∑ci,ji∑ci,j
因此我们就可以维护两组缓存变量vt(r)∈Rm,vt(c)∈Rn, 代表gt2低秩分解后的结果, 解析解保证了vt(r)vt(c)点乘于原始二阶动量gt2之间在广义KL散度度量下的最大近似性. 因此AdaFactor优化器的计算流程如下:
⎩⎨⎧gi,j;t=∇θL(θi,j;t)vi;t(r)=β2vt−1;i(r)+(1−β2)j∑(gi,j;t2+ϵ)vj;t(c)=β2vt−1;j(c)+(1−β2)i∑(gi,j;t2+ϵ)vi,j;t=vi;t(r)vj;t(c)/j∑vj;t(c)v^t=vt/(1−β2t)θt=θt−1−αtgt/v^t vt(r)∈Rm,vt(c)∈Rn两变量的更新逻辑, 保证了两变量的非负性, 使得不等式xlogx≥x−1(∀x>0)始终满足条件, 解析解成立, 得到理论保证的近似效果, 进一步保证了优化的效果.
参考资料