优化算法
TODO
Gradient Descent + Momentum
加上 Momentum 以后,每一次我们在移动我们的参数的时候,我们不是只往 Gradient Descen 的反方向来移动参数,我们是 Gradient 的反方向,加上前一步移动的方向,两者加起来的结果,去调整参数。
确定一个初始的参数,然后我们假设前一步的参数的 Update 量为 0
$$
m^0 = 0
$$
接下来在 $\theta^0$ 的地方,计算 Gradient 的方向$g^0$
然后接下来你要决定下一步要怎么走,它是 Gradient 的方向加上前一步的方向,不过因为前一步正好是 0,现在是刚初始的时候所以前一步是 0,所以 Update 的方向,跟原来的 Gradient Descent 是一样的。
$$
m^1 = {\lambda}m^0-{\eta}g^0\
\theta^1 = \theta^0 + m^1
$$
但从第二步开始,有加上 Momentum 以后就不太一样了,从第二步开始,我们计算 $g^1$,然后接下来我们 Update 的方向,不是 $g^1$ 反方向,而是根据上一次 Update 方向,也就是 m1 减掉 g1,当做我们新的 Update 的方向,这边写成 m2
$$
m^2 = {\lambda}m^1-{\eta}g^1
$$
g1 告诉我们,Gradient 告诉我们要往红色反方向这边走,但是我们不是只听 Gradient 的话,加上 Momentum 以后,我们不是只根据 Gradient 的反方向,来调整我们的参数,我们也会看前一次 Update 的方向
- 如果前一次说要往$m^1$蓝色及蓝色虚线这个方向走
- Gradient 说要往红色反方向这个方向走
- 把两者相加起来,走两者的折中,也就是往蓝色$m^2$ 一个方向走,所以我们就移动了 $m^2$,走到 $\theta^2$ 这个地方
Adaptive Learning Rate
如果在某一个方向上,我们的 gradient 的值很小,非常的平坦,那我们会希望 learning rate 调大一点,如果在某一个方向上非常的陡峭,坡度很大,那我们其实期待,learning rate 可以设得小一点。
我们要改一下,gradient descend 原来的式子,我们只放某一个参数 update 的式子
$$
{\theta{_i}{^{t+1}}} ← {\theta{_i}{^{t}}}-{\eta}{g{_i}{^{t}}}
$$
我们只看一个参数,这个参数叫做${\theta{_i}{^{t}}}$,这个${\theta{_i}{^{t}}}$ 第 t 个 iteration 的值,减掉在第 t 个 iteration 这个参数$ i $ 出来的 gradient ${g{_i}{^{t}}}$
$$
{g{i}{^{t}}}=\frac{\partial{L}}{\partial{\theta_i}}|{\theta=\theta^t}
$$
这个${g{_i}{^{t}}}$ 表在第 t 个 iteration,也就是 $\theta$ 等于$\theta^t$ 的时候,参数 $\theta_i$ 对 loss 的微分,我们把这个 $\theta_i^t$ 减掉 learning rate,乘上 $g_i^t$ 更新 learning rate 到$\theta_i^{t+1}$,这是我们原来的 gradient descend,我们的 learning rate 是固定的
现在我们要有一个随著参数客製化的 learning rate,我们把原来 learning rate $η$ 一项呢,改写成$\frac{η}{\sigma{_i}{^t}}$
$$
{\theta{_i}{^{t+1}}} ← {\theta{_i}{^t}}-{\frac{η}{\sigma{_i}{^t}}}{g{_i}{^{t}}}
$$
这个$\sigma{_i}{^t}$ 发现它有一个上标 t,有一个下标 i,这代表说这个$\sigma$ 个参数,首先它是 depend on i 的,不同的参数我们要给它不同的 σ,同时它也是 iteration dependent 的,不同的 iteration 我们也会有不同的 σ
所以当我们把我们的 learning rate,从 η 改成$\frac{η}{\sigma{_i}{^t}}$ 时候,我们就有一个,parameter dependent 的 learning rate,接下来我们是要看说,这个 parameter dependent 的 learning rate 有什么常见的计算方式?
Root mean square
$\sigma$ 个常见的类型是算,gradient 的 Root Mean Square
现在参数要 update 的式子,我们从 \thetaᵢ⁰ 初始化参数减掉 gᵢ⁰,乘上 learning rate η 除以 σᵢ⁰,就得到 \thetaᵢ¹,
$$
{\theta{_i}{^{1}}} ← {\theta{_i}{^{0}}}-{\frac{η}{σᵢ^0}}{g{_i}{^{0}}}
$$
这个σᵢ⁰在第一次 update 参数的时候,这个 σᵢ⁰ 是(gᵢ⁰)² 开根号
$$
{σᵢ^0}=\sqrt{({g{_i}{^{0}}})^2}=|{g{_i}{^{0}}}|
$$这个 gᵢ⁰ 就是我们的 gradient,就是 gradient 的平方开根号,其实就是 gᵢ⁰ 的绝对值,所以你把 gᵢ⁰ 的绝对值代到${\theta{_i}{^{1}}} ← {\theta{_i}{^{0}}}-{\frac{η}{σᵢ^0}}{g{_i}{^{0}}}$,这个式子中 gᵢ⁰ 跟这个根号底下的 gᵢ⁰,它们的大小是一样的,所以式子中这一项只会有一个,要嘛是正一 要嘛是负一,就代表说我们第一次在 update 参数,从 \thetaᵢ⁰update 到 \thetaᵢ¹ 的时候,要嘛是加上 η 要嘛是减掉 η,跟这个 gradient 的大小没有关係,是看你 η 设多少,这个是第一步的状况
重点是接下来怎么处理,那 \thetaᵢ¹ 它要一样,减掉 gradient gᵢ¹ 乘上 η 除以 σᵢ¹,
$$
{\theta{_i}{^{1}}}-{\frac{η}{σᵢ^1}}{g{_i}{^{1}}}
$$现在在第二次 update 参数的时候,是要除以 σᵢ¹ ,这个 σᵢ¹ 就是我们过去,所有计算出来的 gradient,它的平方的平均再开根号
$$
{σᵢ^1}=\sqrt{\frac{1}{2}[{(g{_i}{^{0}}})^2+{(g{_i}{^{1}}})^2]}
$$我们到目前为止,在第一次 update 参数的时候,我们算出了 gᵢ⁰,在第二次 update 参数的时候,我们算出了 gᵢ¹,所以这个 σᵢ¹ 就是(gᵢ⁰)²,加上(gᵢ¹)² 除以 ½ 再开根号,这个就是 Root Mean Square,我们算出这个 σᵢ¹ 以后,我们的 learning rate 就是 η 除以 σᵢ¹,然后把 \thetaᵢ¹ 减掉,η 除以 σᵢ¹ 乘以 gᵢ¹ 得到 \thetaᵢ²
$$
{\theta{_i}{^{2}}} ← {\theta{_i}{^{1}}}-{\frac{η}{σᵢ^1}}{g{_i}{^{1}}}
$$同样的操作就反覆继续下去,在 \thetaᵢ² 的地方,你要减掉 η 除以 σᵢ² 乘以 gᵢ²,
$$
{\theta{_i}{^{2}}}-{\frac{η}{σᵢ^2}}{g{_i}{^{2}}}
$$那这个$\sigma$ 什么呢,这个 σᵢ² 就是过去,所有算出来的 gradient,它的平方和的平均再开根号
$$
{σᵢ^2}=\sqrt{\frac{1}{3}[{(g{_i}{^{0}}})^2+{(g{_i}{^{1}}})^2+{(g{_i}{^{2}}})^2]}
$$所以你把 gᵢ⁰ 取平方,gᵢ¹ 取平方 gᵢ² 取平方,的平均再开根号,得到 σᵢ² 放在这个地方,然后 update 参数
$$
{\theta{_i}{^{3}}} ← {\theta{_i}{^{2}}}-{\frac{η}{σᵢ^2}}{g{_i}{^{2}}}
$$所以这个 process 这个过程,就反覆继续下去,到第 t 次 update 参数的时候,其实这个是第 t + 1 次,第 t + 1 次 update 参数的时候,你的这个 \sigma{_i}{^t} 它就是过去所有的 gradient,gᵢᵗ 从第一步到目前为止,所有算出来的 gᵢᵗ 的平方和,再平均 再开根号得到 \sigma{i}{^t},
$$
{σᵢ^t}=\sqrt{\frac{1}{t+1}\sum{i=0}^{t}{(g{_i}{^{t}}})^2}
$$
然后在把它除 learning rate,然后用这一项当作是,新的 learning rate 来 update 你的参数,
$$
{\theta{_i}{^{t+1}}} ← {\theta{_i}{^{t}}}-{\frac{η}{σᵢ^t}}{g{_i}{^{t}}}
$$
Adagrad
那这一招被用在一个叫做Adagrad的方法裡面,为什么这一招可以做到我们刚才讲的,坡度比较大的时候,learning rate 就减小,坡度比较小的时候,learning rate 就放大呢?
你可以想像说,现在我们有两个参数:一个叫 \thetaᵢ¹ 一个叫 \thetaᵢ² \thetaᵢ¹ 坡度小 \thetaᵢ² 坡度大
- \thetaᵢ¹ 因为它坡度小,所以你在 \thetaᵢ¹ 这个参数上面,算出来的 gradient 值都比较小
- 因为 gradient 算出来的值比较小,然后这个$\sigma$ gradient 的平方和取平均再开根号
$$
{σᵢ^t}=\sqrt{\frac{1}{t+1}\sum_{i=0}^{t}{(g{_i}{^{t}}})^2}
$$
- 所以算出来的$\sigma$ 小,σ 小 learning rate 就大
$$
{\frac{η}{σᵢ^t}}
$$
反过来说 \thetaᵢ²,\thetaᵢ² 是一个比较陡峭的参数,在 \thetaᵢ² 这个方向上 loss 的变化比较大,所以算出来的 gradient 都比较大,,你的$\sigma$ 比较大,你在 update 的时候 你的 step,你的参数 update 的量就比较小
所以有了$\sigma$ 一项以后,你就可以随著 gradient 的不同,每一个参数的 gradient 的不同,来自动的调整 learning rate 的大小,那这个并不是,你今天会用的最终极的版本,
RMSProp
刚才那个版本,就算是同一个参数,它需要的 learning rate,也会随著时间而改变,我们刚才的假设,好像是同一个参数,它的 gradient 的大小,就会固定是差不多的值,但事实上并不一定是这个样子的
举例来说我们来看,这个新月形的 error surface
如果我们考虑横轴的话,考虑左右横的水平线的方向的话,你会发现说,在绿色箭头这个地方坡度比较陡峭,所以我们需要比较小的 learning rate,
但是走到了中间这一段,到了红色箭头的时候呢,坡度又变得平滑了起来,平滑了起来就需要比较大的 learning rate,所以就算是同一个参数同一个方向,我们也期待说,learning rate 是可以动态的调整的,于是就有了一个新的招数,这个招数叫做RMS Prop
RMS Prop 这个方法有点传奇,它传奇的地方在于它找不到论文,非常多年前应该是将近十年前,Hinton 在 Coursera 上,开过 deep learning 的课程,那个时候他在他的课程裡面,讲了 RMS Prop 这个方法,然后这个方法没有论文,所以你要 cite 的话,你要 cite 那个影片的连结,这是个传奇的方法叫做 RMS Prop
RMS Prop 这个方法,它的第一步跟刚才讲的 Root Mean Square,也就是那个 Apagrad 的方法,是一模一样的
$$
{σᵢ^0}=\sqrt{({g_i^0})^2}
$$
我们看第二步,一样要算出 σᵢ¹,只是我们现在算出 σᵢ¹ 的方法跟刚才,算 Root Mean Square 的时候不一样,刚才在算 Root Mean Square 的时候,每一个 gradient 都有同等的重要性,但在 RMS Prop 裡面,它决定你可以自己调整,现在的这个 gradient,你觉得它有多重要
$$
{σᵢ^1}=\sqrt[]{\alpha(σ_i^0)^2+(1-\alpha)(g_i^1)^2}
$$
所以在 RMS Prop 裡面,我们这个 σᵢ¹ 它是前一步算出来的 σᵢ⁰,裡面就是有 gᵢ⁰,所以这个σᵢ⁰ 就代表了 gᵢ⁰ 的大小,所以它是(σᵢ⁰)²,乘上 α 加上(1-α),乘上现在我们刚算出来的,新鲜热腾腾的 gradient 就是 gᵢ¹
那这个α 就像 learning rate 一样,这个你要自己调它,它是一个 hyperparameter
- 如果我今天α 设很小趋近于 0,就代表我觉得gᵢ¹ 相较于之前所算出来的 gradient 而言,比较重要
- 我α 设很大趋近于 1,那就代表我觉得现在算出来的 gᵢ¹ 比较不重要,之前算出来的 gradient 比较重要
所以同理在第三次 update 参数的时候,我们要算 σᵢ² ,我们就把 σᵢ¹ 拿出来取平方再乘上 α,那 σᵢ¹ 裡面有 gᵢ¹ 跟 σᵢ⁰ ,σᵢ⁰ 裡面又有 gᵢ⁰,所以你知道 σᵢ¹ 裡面它有 gᵢ¹ 有 gᵢ⁰, 然后这个 gᵢ¹ 跟 gᵢ⁰ 呢他们会被乘上 α,然后再加上 1-α 乘上这个(gᵢ²)²
$$
{σᵢ^2}=\sqrt[]{\alpha(σ_i^1)^2+(1-\alpha)(g_i^2)^2}
$$
所以这个 α 就会决定说 gᵢ²,它在整个 σᵢ² 裡面佔有多大的影响力
那同样的过程就反覆继续下去,\sigma{_i}{^t} 等于根号 α 乘上(\sigma{_i}{^t}⁻¹)²,加上(1-α) (gᵢᵗ)²,
$$
{σᵢ^t}=\sqrt[]{\alpha(σ_i^{t-1})^2+(1-\alpha)(g_i^t)^2}
$$
你用 α 来决定现在刚算出来的 gᵢᵗ,它有多重要,好那这个就是 RMSProp
那 RMSProp 我们刚刚讲过说,透过 α 这一项你可以决定说,gᵢᵗ 相较于之前存在,\sigma{_i}{^t}⁻¹ 裡面的 gᵢᵗ 到 gᵢᵗ⁻¹ 而言,它的重要性有多大,如果你用 RMS Prop 的话,你就可以动态调整$\sigma$ 一项,我们现在假设从这个地方开始
这个黑线是我们的 error surface,从这个地方开始你要 update 参数,好你这个球就从这边走到这边,那因为一路上都很平坦,很平坦就代表说 g 算出来很小,代表现在 update 参数的时候,我们会走比较大的步伐
接下来继续滚,滚到这边以后我们 gradient 变大了,如果不是 RMS Prop,原来的 Adagrad 的话它反应比较慢,但如果你用 RMS Prop,然后呢你把 α 设小一点,你就是让新的,刚看到的 gradient 影响比较大的话,那你就可以很快的让$\sigma$ 值变大,也可以很快的让你的步伐变小
你就可以踩一个煞车,本来很平滑走到这个地方,突然变得很陡,那 RMS Prop 可以很快的踩一个煞车,把 learning rate 变小,如果你没有踩剎车的话,你走到这裡这个地方,learning rate 太大了,那 gradient 又很大,两个很大的东西乘起来,你可能就很快就飞出去了,飞到很远的地方
如果继续走,又走到平滑的地方了,因为这个 \sigma{_i}{^t} 你可以调整 α,让它比较看重于,最近算出来的 gradient,所以你 gradient 一变小,σ 可能就反应很快,它的这个值就变小了,然后呢你走的步伐就变大了,这个就是 RMS Prop,
Adam
那今天你最常用的,optimization 的策略,有人又叫做 optimizer,今天最常用的 optimization 的策略,就是 Adam
Adam 就是 RMS Prop 加上 Momentum,那 Adam 的演算法跟原始的论文https://arxiv.org/pdf/1412.6980.pdf
今天 pytorch 裡面,都帮你写得好好的了,所以这个你今天,不用担心这种 optimization 的问题,optimizer 这个 deep learning 的套件,往往都帮你做好了,然后这个 optimizer 裡面,也有一些参数需要调,也有一些 hyperparameter,需要人工决定,但是你往往用预设的,那一种参数就够好了,你自己调有时候会调到比较差的,往往你直接 copy,这个 pytorch 裡面,Adam 这个 optimizer,然后预设的参数不要随便调,就可以得到不错的结果了,关于 Adam 的细节,就留给大家自己研究
Learning Rate Scheduling
我们刚才讲说这个简单的 error surface,我们都 train 不起来,现在我们来看一下,加上 Adaptive Learning Rate 以后,train 不 train 得起来,
那这边是採用最原始的 Adagrad 那个做法啦,就是把过去看过的,这个 learning rate 通通都,过去看过的 gradient,通通都取平方再平均再开根号当作这个$\sigma$,做起来是这个样子的
这个走下来没有问题,然后接下来在左转的时候,这边也是 update 了十万次,之前 update 了十万次,只卡在左转这个地方
那现在有 Adagrad 以后,你可以再继续走下去,走到非常接近终点的位置,因为当你走到这个地方的时候,你因为这个左右的方向的,这个 gradient 很小,所以 learning rate 会自动调整,左右这个方向的,learning rate 会自动变大,所以你这个步伐就可以变大,就可以不断的前进
接下来的问题就是,为什么快走到终点的时候突然爆炸了呢
你想想看 我们在做这个$\sigma$ 时候,我们是把过去所有看到的 gradient,都拿来作平均
所以这个纵轴的方向,在这个初始的这个地方,感觉 gradient 很大
可是这边走了很长一段路以后,这个纵轴的方向,gradient 算出来都很小,所以纵轴这个方向,这个 y 轴的方向就累积了很小的 σ
因为我们在这个 y 轴的方向,看到很多很小的 gradient,所以我们就累积了很小的 σ,累积到一个地步以后,这个 step 就变很大,然后就爆走就喷出去了
喷出去以后没关係,有办法修正回来,因为喷出去以后,就走到了这个 gradient 比较大的地方,走到 gradient 比较大的地方以后,这个$\sigma$ 慢慢的变大,σ 慢慢变大以后,这个参数 update 的距离,Update 的步伐大小就慢慢的变小
你就发现说走著走著,突然往左右喷了一下,但是这个喷了一下不会永远就是震盪,不会做简谐运动停不下来,这个力道慢慢变小,有摩擦力 让它慢慢地慢慢地,又回到中间这个峡谷来,然后但是又累计一段时间以后 又会喷,然后又慢慢地回来 怎么办呢,有一个方法也许可以解决这个问题,这个叫做 learning rate 的 scheduling
什么是 learning rate 的 scheduling 呢
我们刚才这边还有一项 η,这个 η 是一个固定的值,learning rate scheduling 的意思就是说,我们不要把 η 当一个常数,我们把它跟时间有关
最常见的策略叫做Learning Rate Decay,也就是说,随著时间的不断地进行,随著参数不断的 update,我们这个 η 让它越来越小
那这个也就合理了,因为一开始我们距离终点很远,随著参数不断 update,我们距离终点越来越近,所以我们把 learning rate 减小,让我们参数的更新踩了一个煞车,让我们参数的更新能够慢慢地慢下来,所以刚才那个状况,如果加上 Learning Rate Decay 有办法解决
刚才那个状况,如果加上 Learning Rate Decay 的话,我们就可以很平顺的走到终点,因为在这个地方,这个 η 已经变得非常的小了,虽然说它本来想要左右乱喷,但是因为乘上这个非常小的 η,就停下来了 就可以慢慢地走到终点,那除了 Learning Rate Decay 以外,还有另外一个经典,常用的 Learning Rate Scheduling 的方式,叫做Warm Up
Warm Up 这个方法,听起来有点匪夷所思,这 Warm Up 的方法是让 learning rate,要先变大后变小,你会问说变大要变到多大呢,变大速度要多快呢 ,小速度要多快呢,这个也是 hyperparameter,你要自己用手调的,但是大方向的大策略就是,learning rate 要先变大后变小,那这个方法听起来很神奇,就是一个黑科技这样,这个黑科技出现在,很多远古时代的论文裡面
这个 warm up,最近因为在训练 BERT 的时候,往往需要用到 Warm Up,所以又被大家常常拿出来讲,但它并不是有 BERT 以后,才有 Warm Up 的,Warm Up 这东西远古时代就有了,举例来说,Residual Network 裡面是有 Warm Up 的
这边是放了 Residual network,放在 arXiv 上面的文章连结啦,今天这种有关 machine learning 的,文章往往在投 conference 之前,投国际会议之前,就先放到一个叫做 arXiv 的网站上,把它公开来让全世界的人都可以看
你其实看这个 arXiv 的网址,你就可以知道,这篇文章是什么时候放到网路上的,怎么看呢 arXiv 的前四个数字,这 15 代表年份,代表说 residual network 这篇文章,是 2015 年放到 arXiv 上面的,后两个数字代表月份,所以它是 15 年的 12 月,15 年的年底放在 arXiv 上面的
所以五六年前的文章,在 deep learning 变化,这么快速的领域裡面,五六年前就是上古时代,那在上古时代,这个 Residual Network 裡面,就已经记载了 Warm Up 的这件事情,它说我们用 learning rate 0.01,取 Warm Up,先用 learning rate 0.01,再把 learning rate 改成 0.1
用过去我们通常最常见的 train,Learning Rate Scheduling 的方法,就是让 learning rate 越来越小,但是 Residual Network,这边特别註明它反其道而行,一开始要设 0.01 接下来设 0.1,还特别加一个註解说,一开始就用 0.1 反而就 train 不好,不知道为什么 也没解释,反正就是 train 不好,需要 Warm Up 这个黑科技。
而在这个黑科技,在知名的 Transformer 裡面(这门课也会讲到),也用一个式子提了它
它这边有一个式子说,它的 learning rate 遵守这一个,神奇的 function 来设定,它的 learning rate,这个神奇的 function,乍看之下会觉得 哇 在写什么,不知道在写些什么
这个东西你实际上,把这个 function 画出来,你实际上把 equation 画出来的话,就会发现它就是 Warm Up,learning rate 会先增加,然后接下来再递减
所以你发现说 Warm Up 这个技术,在很多知名的 network 裡面都有,被当作一个黑科技,就论文裡面不解释说,为什么要用这个,但就偷偷在一个小地方,你没有注意到的小地方告诉你说,这个 network 要用这种黑科技,才能够把它训练起来
那为什么需要 warm Up 呢,这个仍然是今天,一个可以研究的问题啦
这边有一个可能的解释是说,你想想看当我们在用 Adam RMS Prop,或 Adagrad 的时候,我们会需要计算 σ,它是一个统计的结果,σ 告诉我们,某一个方向它到底有多陡,或者是多平滑,那这个统计的结果,要看得够多笔数据以后,这个统计才精準,所以一开始我们的统计是不精準的
一开始我们的$\sigma$ 不精準的,所以我们一开始不要让我们的参数,走离初始的地方太远,先让它在初始的地方呢,做一些像是探索这样,所以一开始 learning rate 比较小,是让它探索 收集一些有关 error surface 的情报,先收集有关$\sigma$ 统计数据,等$\sigma$ 计得比较精準以后,在让 learning rate 呢慢慢地爬升
所以这是一个解释,为什么我们需要 warm up 的可能性,那如果你想要学更多,有关 warm up 的东西的话,你其实可以看一篇 paper,它是 Adam 的进阶版叫做 RAdam,裡面对 warm up 这件事情,有更多的理解
Mini-batch 梯度下降(Mini-batch gradient descent)
本周将学习优化算法,这能让你的神经网络运行得更快。机器学习的应用是一个高度依赖经验的过程,伴随着大量迭代的过程,你需要训练诸多模型,才能找到合适的那一个,所以,优化算法能够帮助你快速训练模型。
其中一个难点在于,深度学习没有在大数据领域发挥最大的效果,我们可以利用一个巨大的数据集来训练神经网络,而在巨大的数据集基础上进行训练速度很慢。因此,你会发现,使用快速的优化算法,使用好用的优化算法能够大大提高你和团队的效率,那么,我们首先来谈谈mini-batch梯度下降法。
你之前学过,向量化能够让你有效地对所有$m$ 样本进行计算,允许你处理整个训练集,而无需某个明确的公式。所以我们要把训练样本放大巨大的矩阵$X$ 中去,$X= \lbrack x^{(1)}\ x^{(2)}\ x^{(3)}\ldots\ldots x^{(m)}\rbrack$,$Y$ 是如此,$Y= \lbrack y^{(1)}\ y^{(2)}\ y^{(3)}\ldots \ldots y^{(m)}\rbrack$,所以$X$ 维数是$(n_{x},m)$,$Y$ 维数是$(1,m)$,向量化能够让你相对较快地处理所有$m$ 样本。如果$m$ 大的话,处理速度仍然缓慢。比如说,如果$m$ 500 万或 5000 万或者更大的一个数,在对整个训练集执行梯度下降法时,你要做的是,你必须处理整个训练集,然后才能进行一步梯度下降法,然后你需要再重新处理 500 万个训练样本,才能进行下一步梯度下降法。所以如果你在处理完整个 500 万个样本的训练集之前,先让梯度下降法处理一部分,你的算法速度会更快,准确地说,这是你可以做的一些事情。
你可以把训练集分割为小一点的子集训练,这些子集被取名为mini-batch,假设每一个子集中只有 1000 个样本,那么把其中的$x^{(1)}$ $x^{(1000)}$ 出来,将其称为第一个子训练集,也叫做mini-batch,然后你再取出接下来的 1000 个样本,从$x^{(1001)}$ $x^{(2000)}$,然后再取 1000 个样本,以此类推。
接下来我要说一个新的符号,把$x^{(1)}$ $x^{(1000)}$ 为$X^{{1}}$,$x^{(1001)}$ $x^{(2000)}$ 为$X^{{2}}$,如果你的训练样本一共有 500 万个,每个mini-batch都有 1000 个样本,也就是说,你有 5000 个mini-batch,因为 5000 乘以 1000 就是 5000 万。
你共有 5000 个mini-batch,所以最后得到是$X^{\left{ 5000 \right}}$
对$Y$ 要进行相同处理,你也要相应地拆分$Y$ 训练集,所以这是$Y^{{1}}$,然后从$y^{(1001)}$ $y^{(2000)}$,这个叫$Y^{{2}}$,一直到$Y^{{ 5000}}$。
mini-batch的数量$t$ 成了$X^{{ t}}$ $Y^{{t}}$,这就是 1000 个训练样本,包含相应的输入输出对。
在继续课程之前,先确定一下我的符号,之前我们使用了上角小括号$(i)$ 示训练集里的值,所以$x^{(i)}$ 第$i$ 训练样本。我们用了上角中括号$[l]$ 表示神经网络的层数,$z^{\lbrack l\rbrack}$ 示神经网络中第$l$ 的$z$ ,我们现在引入了大括号${t}$ 代表不同的mini-batch,所以我们有$X^{{ t}}$ $Y^{{ t}}$,检查一下自己是否理解无误。
$X^{{ t}}$ $Y^{{ t}}$ 维数:如果$X^{{1}}$ 一个有 1000 个样本的训练集,或者说是 1000 个样本的$x$ ,所以维数应该是$(n_{x},1000)$,$X^{{2}}$ 维数应该是$(n_{x},1000)$,以此类推。因此所有的子集维数都是$(n_{x},1000)$,而这些($Y^{{ t}}$)的维数都是$(1,1000)$。
解释一下这个算法的名称,batch梯度下降法指的是我们之前讲过的梯度下降法算法,就是同时处理整个训练集,这个名字就是来源于能够同时看到整个batch训练集的样本被处理,这个名字不怎么样,但就是这样叫它。
相比之下,mini-batch梯度下降法,指的是我们在下一张幻灯片中会讲到的算法,你每次同时处理的单个的mini-batch $X^{{t}}$ $Y^{{ t}}$,而不是同时处理全部的$X$ $Y$ 练集。
那么究竟mini-batch梯度下降法的原理是什么?在训练集上运行mini-batch梯度下降法,你运行for t=1……5000,因为我们有 5000 个各有 1000 个样本的组,在for循环里你要做得基本就是对$X^{{t}}$ $Y^{{t}}$ 行一步梯度下降法。假设你有一个拥有 1000 个样本的训练集,而且假设你已经很熟悉一次性处理完的方法,你要用向量化去几乎同时处理 1000 个样本。
首先对输入也就是$X^{{ t}}$,执行前向传播,然后执行$z^{\lbrack 1\rbrack} =W^{\lbrack 1\rbrack}X + b^{\lbrack 1\rbrack}$,之前我们这里只有,但是现在你正在处理整个训练集,你在处理第一个mini-batch,在处理mini-batch时它变成了$X^{{ t}}$,即$z^{\lbrack 1\rbrack} = W^{\lbrack 1\rbrack}X^{{ t}} + b^{\lbrack1\rbrack}$,然后执行$A^{[1]k} =g^{[1]}(Z^{[1]})$,之所以用大写的$Z$ 因为这是一个向量内涵,以此类推,直到$A^{\lbrack L\rbrack} = g^{\left\lbrack L \right\rbrack}(Z^{\lbrack L\rbrack})$,这就是你的预测值。注意这里你需要用到一个向量化的执行命令,这个向量化的执行命令,一次性处理 1000 个而不是 500 万个样本。接下来你要计算损失成本函数$J$,因为子集规模是 1000,$J= \frac{1}{1000}\sum_{i = 1}^{l}{L(\hat y^{(i)},y^{(i)})}$,说明一下,这($L(\hat y^{(i)},y^{(i)})$)指的是来自于mini-batch$X^{{ t}}$ $Y^{{t}}$ 的样本。
如果你用到了正则化,你也可以使用正则化的术语,$J =\frac{1}{1000}\sum_{i = 1}^{l}{L(\hat y^{(i)},y^{(i)})} +\frac{\lambda}{2 1000}\sum_{l}^{}{||w^{[l]}||}{F}^{2}$,因为这是一个mini-batch的损失,所以我将$J$ 失记为上角标$t$,放在大括号里($J^{{t}} = \frac{1}{1000}\sum{i = 1}^{l}{L(\hat y^{(i)},y^{(i)})} +\frac{\lambda}{2 1000}\sum_{l}^{}{||w^{[l]}||}_{F}^{2}$)。
你也会注意到,我们做的一切似曾相识,其实跟之前我们执行梯度下降法如出一辙,除了你现在的对象不是$X$,$Y$,而是$X^{{t}}$ $Y^{{ t}}$。接下来,你执行反向传播来计算$J^{{t}}$ 梯度,你只是使用$X^{{ t}}$ $Y^{{t}}$,然后你更新加权值,$W$ 际上是$W^{\lbrack l\rbrack}$,更新为$W^{[l]}:= W^{[l]} - adW^{[l]}$,对$b$ 相同处理,$b^{[l]}:= b^{[l]} - adb^{[l]}$。这是使用mini-batch梯度下降法训练样本的一步,我写下的代码也可被称为进行“一代”(1 epoch)的训练。一代这个词意味着只是一次遍历了训练集。
使用batch梯度下降法,一次遍历训练集只能让你做一个梯度下降,使用mini-batch梯度下降法,一次遍历训练集,能让你做 5000 个梯度下降。当然正常来说你想要多次遍历训练集,还需要为另一个while循环设置另一个for循环。所以你可以一直处理遍历训练集,直到最后你能收敛到一个合适的精度。
如果你有一个丢失的训练集,mini-batch梯度下降法比batch梯度下降法运行地更快,所以几乎每个研习深度学习的人在训练巨大的数据集时都会用到,下一个视频中,我们将进一步深度讨论mini-batch梯度下降法,你也会因此更好地理解它的作用和原理。
理解 mini-batch 梯度下降法(Understanding mini-batch gradient descent)
在上周视频中,你知道了如何利用mini-batch梯度下降法来开始处理训练集和开始梯度下降,即使你只处理了部分训练集,即使你是第一次处理,本视频中,我们将进一步学习如何执行梯度下降法,更好地理解其作用和原理。
使用batch梯度下降法时,每次迭代你都需要历遍整个训练集,可以预期每次迭代成本都会下降,所以如果成本函数$J$ 迭代次数的一个函数,它应该会随着每次迭代而减少,如果$J$ 某次迭代中增加了,那肯定出了问题,也许你的学习率太大。
使用mini-batch梯度下降法,如果你作出成本函数在整个过程中的图,则并不是每次迭代都是下降的,特别是在每次迭代中,你要处理的是$X^{{t}}$ $Y^{{ t}}$,如果要作出成本函数$J^{{ t}}$ 图,而$J^{{t}}$ 和$X^{{ t}}$,$Y^{{t}}$ 关,也就是每次迭代下你都在训练不同的样本集或者说训练不同的mini-batch,如果你要作出成本函数$J$ 图,你很可能会看到这样的结果,走向朝下,但有更多的噪声,所以如果你作出$J^{{t}}$ 图,因为在训练mini-batch梯度下降法时,会经过多代,你可能会看到这样的曲线。没有每次迭代都下降是不要紧的,但走势应该向下,噪声产生的原因在于也许$X^{{1}}$ $Y^{{1}}$ 比较容易计算的mini-batch,因此成本会低一些。不过也许出于偶然,$X^{{2}}$ $Y^{{2}}$ 比较难运算的mini-batch,或许你需要一些残缺的样本,这样一来,成本会更高一些,所以才会出现这些摆动,因为你是在运行mini-batch梯度下降法作出成本函数图。
你需要决定的变量之一是mini-batch的大小,$m$ 是训练集的大小,极端情况下,如果mini-batch的大小等于$m$,其实就是batch梯度下降法,在这种极端情况下,你就有了mini-batch $X^{{1}}$ $Y^{{1}}$,并且该mini-batch等于整个训练集,所以把mini-batch大小设为$m$ 以得到batch梯度下降法。
另一个极端情况,假设mini-batch大小为 1,就有了新的算法,叫做随机梯度下降法,每个样本都是独立的mini-batch,当你看第一个mini-batch,也就是$X^{{1}}$ $Y^{{1}}$,如果mini-batch大小为 1,它就是你的第一个训练样本,这就是你的第一个训练样本。接着再看第二个mini-batch,也就是第二个训练样本,采取梯度下降步骤,然后是第三个训练样本,以此类推,一次只处理一个。
看在两种极端下成本函数的优化情况,如果这是你想要最小化的成本函数的轮廓,最小值在那里,batch梯度下降法从某处开始,相对噪声低些,幅度也大一些,你可以继续找最小值。
相反,在随机梯度下降法中,从某一点开始,我们重新选取一个起始点,每次迭代,你只对一个样本进行梯度下降,大部分时候你向着全局最小值靠近,有时候你会远离最小值,因为那个样本恰好给你指的方向不对,因此随机梯度下降法是有很多噪声的,平均来看,它最终会靠近最小值,不过有时候也会方向错误,因为随机梯度下降法永远不会收敛,而是会一直在最小值附近波动,但它并不会在达到最小值并停留在此。
实际上你选择的mini-batch大小在二者之间,大小在 1 和$m$ 间,而 1 太小了,$m$ 大了,原因在于如果使用batch梯度下降法,mini-batch的大小为$m$,每个迭代需要处理大量训练样本,该算法的主要弊端在于特别是在训练样本数量巨大的时候,单次迭代耗时太长。如果训练样本不大,batch梯度下降法运行地很好。
相反,如果使用随机梯度下降法,如果你只要处理一个样本,那这个方法很好,这样做没有问题,通过减小学习率,噪声会被改善或有所减小,但随机梯度下降法的一大缺点是,你会失去所有向量化带给你的加速,因为一次性只处理了一个训练样本,这样效率过于低下,所以实践中最好选择不大不小的mini-batch尺寸,实际上学习率达到最快。你会发现两个好处,一方面,你得到了大量向量化,上个视频中我们用过的例子中,如果mini-batch大小为 1000 个样本,你就可以对 1000 个样本向量化,比你一次性处理多个样本快得多。另一方面,你不需要等待整个训练集被处理完就可以开始进行后续工作,再用一下上个视频的数字,每次训练集允许我们采取 5000 个梯度下降步骤,所以实际上一些位于中间的mini-batch大小效果最好。
用mini-batch梯度下降法,我们从这里开始,一次迭代这样做,两次,三次,四次,它不会总朝向最小值靠近,但它比随机梯度下降要更持续地靠近最小值的方向,它也不一定在很小的范围内收敛或者波动,如果出现这个问题,可以慢慢减少学习率,我们在下个视频会讲到学习率衰减,也就是如何减小学习率。
如果mini-batch大小既不是 1 也不是$m$,应该取中间值,那应该怎么选择呢?其实是有指导原则的。
首先,如果训练集较小,直接使用batch梯度下降法,样本集较小就没必要使用mini-batch梯度下降法,你可以快速处理整个训练集,所以使用batch梯度下降法也很好,这里的少是说小于 2000 个样本,这样比较适合使用batch梯度下降法。不然,样本数目较大的话,一般的mini-batch大小为 64 到 512,考虑到电脑内存设置和使用的方式,如果mini-batch大小是 2 的$n$ 方,代码会运行地快一些,64 就是 2 的 6 次方,以此类推,128 是 2 的 7 次方,256 是 2 的 8 次方,512 是 2 的 9 次方。所以我经常把mini-batch大小设成 2 的次方。在上一个视频里,我的mini-batch大小设为了 1000,建议你可以试一下 1024,也就是 2 的 10 次方。也有mini-batch的大小为 1024,不过比较少见,64 到 512 的mini-batch比较常见。
最后需要注意的是在你的mini-batch中,要确保$X^{{ t}}$ $Y^{{t}}$ 符合CPU/GPU内存,取决于你的应用方向以及训练集的大小。如果你处理的mini-batch和CPU/GPU内存不相符,不管你用什么方法处理数据,你会注意到算法的表现急转直下变得惨不忍睹,所以我希望你对一般人们使用的mini-batch大小有一个直观了解。事实上mini-batch大小是另一个重要的变量,你需要做一个快速尝试,才能找到能够最有效地减少成本函数的那个,我一般会尝试几个不同的值,几个不同的 2 次方,然后看能否找到一个让梯度下降优化算法最高效的大小。希望这些能够指导你如何开始找到这一数值。
你学会了如何执行mini-batch梯度下降,令算法运行得更快,特别是在训练样本数目较大的情况下。不过还有个更高效的算法,比梯度下降法和mini-batch梯度下降法都要高效的多,我们在接下来的视频中将为大家一一讲解。
指数加权平均数(Exponentially weighted averages)
我想向你展示几个优化算法,它们比梯度下降法快,要理解这些算法,你需要用到指数加权平均,在统计中也叫做指数加权移动平均,我们首先讲这个,然后再来讲更复杂的优化算法。
虽然现在我生活在美国,实际上我生于英国伦敦。比如我这儿有去年伦敦的每日温度,所以 1 月 1 号,温度是 40 华氏度,相当于 4 摄氏度。我知道世界上大部分地区使用摄氏度,但是美国使用华氏度。在 1 月 2 号是 9 摄氏度等等。在年中的时候,一年 365 天,年中就是说,大概 180 天的样子,也就是 5 月末,温度是 60 华氏度,也就是 15 摄氏度等等。夏季温度转暖,然后冬季降温。
你用数据作图,可以得到以下结果,起始日在 1 月份,这里是夏季初,这里是年末,相当于 12 月末。
这里是 1 月 1 号,年中接近夏季的时候,随后就是年末的数据,看起来有些杂乱,如果要计算趋势的话,也就是温度的局部平均值,或者说移动平均值。
你要做的是,首先使$v_{0} =0$,每天,需要使用 0.9 的加权数之前的数值加上当日温度的 0.1 倍,即$v_{1} =0.9v_{0} + 0.1\theta_{1}$,所以这里是第一天的温度值。
第二天,又可以获得一个加权平均数,0.9 乘以之前的值加上当日的温度 0.1 倍,即$v_{2}= 0.9v_{1} + 0.1\theta_{2}$,以此类推。
第二天值加上第三日数据的 0.1,如此往下。大体公式就是某天的$v$ 于前一天$v$ 的 0.9 加上当日温度的 0.1。
如此计算,然后用红线作图的话,便得到这样的结果。
你得到了移动平均值,每日温度的指数加权平均值。
看一下上一张幻灯片里的公式,$v_{t} = 0.9v_{t - 1} +0.1\theta_{t}$,我们把 0.9 这个常数变成$\beta$,将之前的 0.1 变成$(1 - \beta)$,即$v_{t} = \beta v_{t - 1} + (1 - \beta)\theta_{t}$
由于以后我们要考虑的原因,在计算时可视$v_{t}$ 概是$\frac{1}{(1 -\beta)}$ 每日温度,如果$\beta$ 0.9,你会想,这是十天的平均值,也就是红线部分。
我们来试试别的,将$\beta$ 置为接近 1 的一个值,比如 0.98,计算$\frac{1}{(1 - 0.98)} =50$,这就是粗略平均了一下,过去 50 天的温度,这时作图可以得到绿线。
这个高值$\beta$ 注意几点,你得到的曲线要平坦一些,原因在于你多平均了几天的温度,所以这个曲线,波动更小,更加平坦,缺点是曲线进一步右移,因为现在平均的温度值更多,要平均更多的值,指数加权平均公式在温度变化时,适应地更缓慢一些,所以会出现一定延迟,因为当$\beta=0.98$,相当于给前一天的值加了太多权重,只有 0.02 的权重给了当日的值,所以温度变化时,温度上下起伏,当$\beta$ 较大时,指数加权平均值适应地更缓慢一些。
我们可以再换一个值试一试,如果$\beta$ 另一个极端值,比如说 0.5,根据右边的公式($\frac{1}{(1-\beta)}$),这是平均了两天的温度。
作图运行后得到黄线。
由于仅平均了两天的温度,平均的数据太少,所以得到的曲线有更多的噪声,有可能出现异常值,但是这个曲线能够更快适应温度变化。
所以指数加权平均数经常被使用,再说一次,它在统计学中被称为指数加权移动平均值,我们就简称为指数加权平均数。通过调整这个参数($\beta$),或者说后面的算法学习,你会发现这是一个很重要的参数,可以取得稍微不同的效果,往往中间有某个值效果最好,$\beta$ 中间值时得到的红色曲线,比起绿线和黄线更好地平均了温度。
现在你知道计算指数加权平均数的基本原理,下一个视频中,我们再聊聊它的本质作用。
理解指数加权平均数(Understanding exponentially weighted averages)
上个视频中,我们讲到了指数加权平均数,这是几个优化算法中的关键一环,而这几个优化算法能帮助你训练神经网络。本视频中,我希望进一步探讨算法的本质作用。
回忆一下这个计算指数加权平均数的关键方程。
${{v}{t}}=\beta {{v}{t-1}}+(1-\beta ){{\theta }_{t}}$
$\beta=0.9$ 时候,得到的结果是红线,如果它更接近于 1,比如 0.98,结果就是绿线,如果$\beta$ 一点,如果是 0.5,结果就是黄线。
我们进一步地分析,来理解如何计算出每日温度的平均值。
同样的公式,${{v}{t}}=\beta {{v}{t-1}}+(1-\beta ){{\theta }_{t}}$
使$\beta=0.9$,写下相应的几个公式,所以在执行的时候,$t$ 0 到 1 到 2 到 3,$t$ 值在不断增加,为了更好地分析,我写的时候使得$t$ 值不断减小,然后继续往下写。
首先看第一个公式,理解$v_{100}$ 什么?我们调换一下这两项($0.9v_{99}0.1\theta_{100}$),$v_{100}= 0.1\theta_{100} + 0.9v_{99}$。
那么$v_{99}$ 什么?我们就代入这个公式($v_{99} = 0.1\theta_{99} +0.9v_{98}$),所以:
$v_{100} = 0.1\theta_{100} + 0.9(0.1\theta_{99} + 0.9v_{98})$。
那么$v_{98}$ 什么?你可以用这个公式计算($v_{98} = 0.1\theta_{98} +0.9v_{97}$),把公式代进去,所以:
$v_{100} = 0.1\theta_{100} + 0.9(0.1\theta_{99} + 0.9(0.1\theta_{98} +0.9v_{97}))$。
以此类推,如果你把这些括号都展开,
$v_{100} = 0.1\theta_{100} + 0.1 \times 0.9 \theta_{99} + 0.1 \times {(0.9)}^{2}\theta_{98} + 0.1 \times {(0.9)}^{3}\theta_{97} + 0.1 \times {(0.9)}^{4}\theta_{96} + \ldots$
所以这是一个加和并平均,100 号数据,也就是当日温度。我们分析$v_{100}$ 组成,也就是在一年第 100 天计算的数据,但是这个是总和,包括 100 号数据,99 号数据,97 号数据等等。画图的一个办法是,假设我们有一些日期的温度,所以这是数据,这是$t$,所以 100 号数据有个数值,99 号数据有个数值,98 号数据等等,$t$ 100,99,98 等等,这就是数日的温度数值。
然后我们构建一个指数衰减函数,从 0.1 开始,到$0.1 \times 0.9$,到$0.1 \times {(0.9)}^{2}$,以此类推,所以就有了这个指数衰减函数。
计算$v_{100}$ 通过,把两个函数对应的元素,然后求和,用这个数值 100 号数据值乘以 0.1,99 号数据值乘以 0.1 乘以${(0.9)}^{2}$,这是第二项,以此类推,所以选取的是每日温度,将其与指数衰减函数相乘,然后求和,就得到了$v_{100}$。
结果是,稍后我们详细讲解,不过所有的这些系数($0.10.1 \times 0.90.1 \times {(0.9)}^{2}0.1 \times {(0.9)}^{3}\ldots$),相加起来为 1 或者逼近 1,我们称之为偏差修正,下个视频会涉及。
最后也许你会问,到底需要平均多少天的温度。实际上${(0.9)}^{10}$ 约为 0.35,这大约是$\frac{1}{e}$,e 是自然算法的基础之一。大体上说,如果有$1-\varepsilon$,在这个例子中,$\varepsilon=0.1$,所以$1-\varepsilon=0.9$,${(1-\varepsilon)}^{(\frac{1}{\varepsilon})}$ 等于$\frac{1}{e}$,大约是 0.34,0.35,换句话说,10 天后,曲线的高度下降到$\frac{1}{3}$,相当于在峰值的$\frac{1}{e}$。
又因此当$\beta=0.9$ 时候,我们说仿佛你在计算一个指数加权平均数,只关注了过去 10 天的温度,因为 10 天后,权重下降到不到当日权重的三分之一。
相反,如果,那么 0.98 需要多少次方才能达到这么小的数值?${(0.98)}^{50}$ 约等于$\frac{1}{e}$,所以前 50 天这个数值比$\frac{1}{e}$ ,数值会快速衰减,所以本质上这是一个下降幅度很大的函数,你可以看作平均了 50 天的温度。因为在例子中,要代入等式的左边,$\varepsilon=0.02$,所以$\frac{1}{\varepsilon}$ 50,我们由此得到公式,我们平均了大约$\frac{1}{(1-\beta)}$ 的温度,这里$\varepsilon$ 替了$1-\beta$,也就是说根据一些常数,你能大概知道能够平均多少日的温度,不过这只是思考的大致方向,并不是正式的数学证明。
最后讲讲如何在实际中执行,还记得吗?我们一开始将$v_{0}$ 置为 0,然后计算第一天$v_{1}$,然后$v_{2}$,以此类推。
现在解释一下算法,可以将$v_{0}$,$v_{1}$,$v_{2}$ 等写成明确的变量,不过在实际中执行的话,你要做的是,一开始将$v$ 始化为 0,然后在第一天使$v:= \beta v + (1 - \beta)\theta_{1}$,然后第二天,更新$v$ ,$v: = \beta v + (1 -\beta)\theta_{2}$,以此类推,有些人会把$v$ 下标,来表示$v$ 用来计算数据的指数加权平均数。
再说一次,但是换个说法,$v_{\theta} =0$,然后每一天,拿到第$t$ 的数据,把$v$ 新为$v: = \beta v_{\theta} + (1 -\beta)\theta_{t}$。
指数加权平均数公式的好处之一在于,它占用极少内存,电脑内存中只占用一行数字而已,然后把最新数据代入公式,不断覆盖就可以了,正因为这个原因,其效率,它基本上只占用一行代码,计算指数加权平均数也只占用单行数字的存储和内存,当然它并不是最好的,也不是最精准的计算平均数的方法。如果你要计算移动窗,你直接算出过去 10 天的总和,过去 50 天的总和,除以 10 和 50 就好,如此往往会得到更好的估测。但缺点是,如果保存所有最近的温度数据,和过去 10 天的总和,必须占用更多的内存,执行更加复杂,计算成本也更加高昂。
所以在接下来的视频中,我们会计算多个变量的平均值,从计算和内存效率来说,这是一个有效的方法,所以在机器学习中会经常使用,更不用说只要一行代码,这也是一个优势。
现在你学会了计算指数加权平均数,你还需要知道一个专业概念,叫做偏差修正,下一个视频我们会讲到它,接着你就可以用它构建更好的优化算法,而不是简单直接的梯度下降法。
指数加权平均的偏差修正(Bias correction in exponentially weighted averages)
你学过了如何计算指数加权平均数,有一个技术名词叫做偏差修正,可以让平均数运算更加准确,来看看它是怎么运行的。
${{v}{t}}=\beta {{v}{t-1}}+(1-\beta ){{\theta }_{t}}$
在上一个视频中,这个(红色)曲线对应$\beta$ 值为 0.9,这个(绿色)曲线对应的$\beta$=0.98,如果你执行写在这里的公式,在$\beta$ 于 0.98 的时候,得到的并不是绿色曲线,而是紫色曲线,你可以注意到紫色曲线的起点较低,我们来看看怎么处理。
计算移动平均数的时候,初始化$v_{0} = 0$,$v_{1} = 0.98v_{0} +0.02\theta_{1}$,但是$v_{0} =0$,所以这部分没有了($0.98v_{0}$),所以$v_{1} =0.02\theta_{1}$,所以如果一天温度是 40 华氏度,那么$v_{1} = 0.02\theta_{1} =0.02 \times 40 = 8$,因此得到的值会小很多,所以第一天温度的估测不准。
$v_{2} = 0.98v_{1} + 0.02\theta_{2}$,如果代入$v_{1}$,然后相乘,所以$v_{2}= 0.98 \times 0.02\theta_{1} + 0.02\theta_{2} = 0.0196\theta_{1} +0.02\theta_{2}$,假设$\theta_{1}$ $\theta_{2}$ 是正数,计算后$v_{2}$ 远小于$\theta_{1}$ $\theta_{2}$,所以$v_{2}$ 能很好估测出这一年前两天的温度。
有个办法可以修改这一估测,让估测变得更好,更准确,特别是在估测初期,也就是不用$v_{t}$,而是用$\frac{v_{t}}{1- \beta^{t}}$,t 就是现在的天数。举个具体例子,当$t=2$ ,$1 - \beta^{t} = 1 - {0.98}^{2} = 0.0396$,因此对第二天温度的估测变成了$\frac{v_{2}}{0.0396} =\frac{0.0196\theta_{1} + 0.02\theta_{2}}{0.0396}$,也就是$\theta_{1}$ $\theta_{2}$ 加权平均数,并去除了偏差。你会发现随着$t$ 加,$\beta^{t}$ 近于 0,所以当$t$ 大的时候,偏差修正几乎没有作用,因此当$t$ 大的时候,紫线基本和绿线重合了。不过在开始学习阶段,你才开始预测热身练习,偏差修正可以帮助你更好预测温度,偏差修正可以帮助你使结果从紫线变成绿线。
在机器学习中,在计算指数加权平均数的大部分时候,大家不在乎执行偏差修正,因为大部分人宁愿熬过初始时期,拿到具有偏差的估测,然后继续计算下去。如果你关心初始时期的偏差,在刚开始计算指数加权移动平均数的时候,偏差修正能帮助你在早期获取更好的估测。
所以你学会了计算指数加权移动平均数,我们接着用它来构建更好的优化算法吧!
动量梯度下降法(Gradient descent with Momentum)
还有一种算法叫做Momentum,或者叫做动量梯度下降法,运行速度几乎总是快于标准的梯度下降算法,简而言之,基本的想法就是计算梯度的指数加权平均数,并利用该梯度更新你的权重,在本视频中,我们呢要一起拆解单句描述,看看你到底如何计算。
例如,如果你要优化成本函数,函数形状如图,红点代表最小值的位置,假设你从这里(蓝色点)开始梯度下降法,如果进行梯度下降法的一次迭代,无论是batch或mini-batch下降法,也许会指向这里,现在在椭圆的另一边,计算下一步梯度下降,结果或许如此,然后再计算一步,再一步,计算下去,你会发现梯度下降法要很多计算步骤对吧?
慢慢摆动到最小值,这种上下波动减慢了梯度下降法的速度,你就无法使用更大的学习率,如果你要用较大的学习率(紫色箭头),结果可能会偏离函数的范围,为了避免摆动过大,你要用一个较小的学习率。
另一个看待问题的角度是,在纵轴上,你希望学习慢一点,因为你不想要这些摆动,但是在横轴上,你希望加快学习,你希望快速从左向右移,移向最小值,移向红点。所以使用动量梯度下降法,你需要做的是,在每次迭代中,确切来说在第$t$ 迭代的过程中,你会计算微分$dW$,$db$,我会省略上标$[l]$,你用现有的mini-batch计算$dW$,$db$。如果你用batch梯度下降法,现在的mini-batch就是全部的batch,对于batch梯度下降法的效果是一样的。如果现有的mini-batch就是整个训练集,效果也不错,你要做的是计算$v_{{dW}}= \beta v_{{dW}} + \left( 1 - \beta \right)dW$,这跟我们之前的计算相似,也就是$v = \beta v + \left( 1 - \beta \right)\theta_{t}$,$dW$ 移动平均数,接着同样地计算$v_{db}$,$v_{db} = \beta v_{{db}} + ( 1 - \beta){db}$,然后重新赋值权重,$W:= W -av_{{dW}}$,同样$b:= b - a v_{db}$,这样就可以减缓梯度下降的幅度。
例如,在上几个导数中,你会发现这些纵轴上的摆动平均值接近于零,所以在纵轴方向,你希望放慢一点,平均过程中,正负数相互抵消,所以平均值接近于零。但在横轴方向,所有的微分都指向横轴方向,因此横轴方向的平均值仍然较大,因此用算法几次迭代后,你发现动量梯度下降法,最终纵轴方向的摆动变小了,横轴方向运动更快,因此你的算法走了一条更加直接的路径,在抵达最小值的路上减少了摆动。
动量梯度下降法的一个本质,这对有些人而不是所有人有效,就是如果你要最小化碗状函数,这是碗的形状,我画的不太好。
它们能够最小化碗状函数,这些微分项,想象它们为你从山上往下滚的一个球,提供了加速度,Momentum项相当于速度。
想象你有一个碗,你拿一个球,微分项给了这个球一个加速度,此时球正向山下滚,球因为加速度越滚越快,而因为$\beta$ 稍小于 1,表现出一些摩擦力,所以球不会无限加速下去,所以不像梯度下降法,每一步都独立于之前的步骤,你的球可以向下滚,获得动量,可以从碗向下加速获得动量。我发现这个球从碗滚下的比喻,物理能力强的人接受得比较好,但不是所有人都能接受,如果球从碗中滚下这个比喻,你理解不了,别担心。
最后我们来看具体如何计算,算法在此。
所以你有两个超参数,学习率$a$ 及参数$\beta$,$\beta$ 制着指数加权平均数。$\beta$ 常用的值是 0.9,我们之前平均了过去十天的温度,所以现在平均了前十次迭代的梯度。实际上$\beta$ 0.9 时,效果不错,你可以尝试不同的值,可以做一些超参数的研究,不过 0.9 是很棒的鲁棒数。那么关于偏差修正,所以你要拿$v_{dW}$ $v_{db}$ 以$1-\beta^{t}$,实际上人们不这么做,因为 10 次迭代之后,因为你的移动平均已经过了初始阶段。实际中,在使用梯度下降法或动量梯度下降法时,人们不会受到偏差修正的困扰。当然$v_{{dW}}$ 始值是 0,要注意到这是和$dW$ 有相同维数的零矩阵,也就是跟$W$ 有相同的维数,$v_{db}$ 初始值也是向量零,所以和$db$ 有相同的维数,也就是和$b$ 同一维数。
最后要说一点,如果你查阅了动量梯度下降法相关资料,你经常会看到一个被删除了的专业词汇,$1-\beta$ 删除了,最后得到的是$v_{dW}= \beta v_{{dW}} +dW$。用紫色版本的结果就是,所以$v_{{dW}}$ 小了$1-\beta$ ,相当于乘以$\frac{1}{1- \beta}$,所以你要用梯度下降最新值的话,$a$ 根据$\frac{1}{1 -\beta}$ 应变化。实际上,二者效果都不错,只会影响到学习率$a$ 最佳值。我觉得这个公式用起来没有那么自然,因为有一个影响,如果你最后要调整超参数$\beta$,就会影响到$v_{{dW}}$ $v_{db}$,你也许还要修改学习率$a$,所以我更喜欢左边的公式,而不是删去了$1-\beta$ 这个公式,所以我更倾向于使用左边的公式,也就是有$1-\beta$ 这个公式,但是两个公式都将$\beta$ 置为 0.9,是超参数的常见选择,只是在这两个公式中,学习率$a$ 调整会有所不同。
所以这就是动量梯度下降法,这个算法肯定要好于没有Momentum的梯度下降算法,我们还可以做别的事情来加快学习算法,我们将在接下来的视频中探讨这些问题。
RMSprop
你们知道了动量(Momentum)可以加快梯度下降,还有一个叫做RMSprop的算法,全称是root mean square prop算法,它也可以加速梯度下降,我们来看看它是如何运作的。
回忆一下我们之前的例子,如果你执行梯度下降,虽然横轴方向正在推进,但纵轴方向会有大幅度摆动,为了分析这个例子,假设纵轴代表参数$b$,横轴代表参数$W$,可能有$W_{1}$,$W_{2}$ 者其它重要的参数,为了便于理解,被称为$b$ $W$。
所以,你想减缓$b$ 向的学习,即纵轴方向,同时加快,至少不是减缓横轴方向的学习,RMSprop算法可以实现这一点。
在第$t$ 迭代中,该算法会照常计算当下mini-batch的微分$dW$,$db$,所以我会保留这个指数加权平均数,我们用到新符号$S_{dW}$,而不是$v_{dW}$,因此$S_{dW}= \beta S_{dW} + (1 -\beta) {dW}^{2}$,澄清一下,这个平方的操作是针对这一整个符号的,这样做能够保留微分平方的加权平均数,同样$S_{db}= \beta S_{db} + (1 - \beta){db}^{2}$,再说一次,平方是针对整个符号的操作。
接着RMSprop会这样更新参数值,$W:= W -a\frac{dW}{\sqrt{S_{dW}}}$,$b:=b -\alpha\frac{db}{\sqrt{S_{db}}}$,我们来理解一下其原理。记得在横轴方向或者在例子中的$W$ 向,我们希望学习速度快,而在垂直方向,也就是例子中的$b$ 向,我们希望减缓纵轴上的摆动,所以有了$S_{dW}$ $S_{db}$,我们希望$S_{dW}$ 相对较小,所以我们要除以一个较小的数,而希望$S_{db}$ 较大,所以这里我们要除以较大的数字,这样就可以减缓纵轴上的变化。你看这些微分,垂直方向的要比水平方向的大得多,所以斜率在$b$ 向特别大,所以这些微分中,$db$ 大,$dW$ 小,因为函数的倾斜程度,在纵轴上,也就是 b 方向上要大于在横轴上,也就是$W$ 向上。$db$ 平方较大,所以$S_{db}$ 会较大,而相比之下,$dW$ 小一些,亦或$dW$ 方会小一些,因此$S_{dW}$ 小一些,结果就是纵轴上的更新要被一个较大的数相除,就能消除摆动,而水平方向的更新则被较小的数相除。
RMSprop的影响就是你的更新最后会变成这样(绿色线),纵轴方向上摆动较小,而横轴方向继续推进。还有个影响就是,你可以用一个更大学习率$a$,然后加快学习,而无须在纵轴上垂直方向偏离。
要说明一点,我一直把纵轴和横轴方向分别称为$b$ $W$,只是为了方便展示而已。实际中,你会处于参数的高维度空间,所以需要消除摆动的垂直维度,你需要消除摆动,实际上是参数$W_1$,$W_2$ 的合集,水平维度可能$W_3$,$W_4$ 等,因此把$W$ $b$ 开只是方便说明。实际中$dW$ 一个高维度的参数向量,$db$ 是一个高维度参数向量,但是你的直觉是,在你要消除摆动的维度中,最终你要计算一个更大的和值,这个平方和微分的加权平均值,所以你最后去掉了那些有摆动的方向。所以这就是RMSprop,全称是均方根,因为你将微分进行平方,然后最后使用平方根。
最后再就这个算法说一些细节的东西,然后我们再继续。下一个视频中,我们会将RMSprop和Momentum结合起来,我们在Momentum中采用超参数$\beta$,为了避免混淆,我们现在不用$\beta$,而采用超参数$\beta_{2}$ 保证在Momentum和RMSprop中采用同一超参数。要确保你的算法不会除以 0,如果$S_{dW}$ 平方根趋近于 0 怎么办?得到的答案就非常大,为了确保数值稳定,在实际操练的时候,你要在分母上加上一个很小很小的$\varepsilon$,$\varepsilon$ 多少没关系,$10^{-8}$ 个不错的选择,这只是保证数值能稳定一些,无论什么原因,你都不会除以一个很小很小的数。所以RMSprop跟Momentum有很相似的一点,可以消除梯度下降中的摆动,包括mini-batch梯度下降,并允许你使用一个更大的学习率$a$,从而加快你的算法学习速度。
所以你学会了如何运用RMSprop,这是给学习算法加速的另一方法。关于RMSprop的一个有趣的事是,它首次提出并不是在学术研究论文中,而是在多年前Jeff Hinton在Coursera的课程上。我想Coursera并不是故意打算成为一个传播新兴的学术研究的平台,但是却达到了意想不到的效果。就是从Coursera课程开始,RMSprop开始被人们广为熟知,并且发展迅猛。
我们讲过了Momentum,我们讲了RMSprop,如果二者结合起来,你会得到一个更好的优化算法,在下个视频中我们再好好讲一讲为什么。
Adam 优化算法(Adam optimization algorithm)
在深度学习的历史上,包括许多知名研究者在内,提出了优化算法,并很好地解决了一些问题,但随后这些优化算法被指出并不能一般化,并不适用于多种神经网络,时间久了,深度学习圈子里的人开始多少有些质疑全新的优化算法,很多人都觉得动量(Momentum)梯度下降法很好用,很难再想出更好的优化算法。所以RMSprop以及Adam优化算法(Adam优化算法也是本视频的内容),就是少有的经受住人们考验的两种算法,已被证明适用于不同的深度学习结构,这个算法我会毫不犹豫地推荐给你,因为很多人都试过,并且用它很好地解决了许多问题。
Adam优化算法基本上就是将Momentum和RMSprop结合在一起,那么来看看如何使用Adam算法。
使用Adam算法,首先你要初始化,$v_{dW} = 0$,$S_{dW} =0$,$v_{db} = 0$,$S_{db} =0$,在第$t$ 迭代中,你要计算微分,用当前的mini-batch计算$dW$,$db$,一般你会用mini-batch梯度下降法。接下来计算Momentum指数加权平均数,所以$v_{dW}= \beta_{1}v_{dW} + ( 1 - \beta_{1})dW$(使用$\beta_{1}$,这样就不会跟超参数$\beta_{2}$ 淆,因为后面RMSprop要用到$\beta_{2}$),使用Momentum时我们肯定会用这个公式,但现在不叫它$\beta$,而叫它$\beta_{1}$。同样$v_{db}= \beta_{1}v_{db} + ( 1 -\beta_{1} ){db}$。
接着你用RMSprop进行更新,即用不同的超参数$\beta_{2}$,$S_{dW}=\beta_{2}S_{dW} + ( 1 - \beta_{2}){(dW)}^{2}$,再说一次,这里是对整个微分$dW$ 行平方处理,$S_{db} =\beta_{2}S_{db} + \left( 1 - \beta_{2} \right){(db)}^{2}$。
相当于Momentum更新了超参数$\beta_{1}$,RMSprop更新了超参数$\beta_{2}$。一般使用Adam算法的时候,要计算偏差修正,$v_{dW}^{\text{corrected}}$,修正也就是在偏差修正之后,
$v_{dW}^{\text{corrected}}= \frac{v_{dW}}{1 - \beta_{1}^{t}}$,
同样$v_{db}^{\text{corrected}} =\frac{v_{db}}{1 -\beta_{1}^{t}}$,
$S$ 使用偏差修正,也就是$S_{dW}^{\text{corrected}} =\frac{S_{dW}}{1 - \beta_{2}^{t}}$,$S_{db}^{\text{corrected}} =\frac{S_{db}}{1 - \beta_{2}^{t}}$。
最后更新权重,所以$W$ 新后是$W:= W - \frac{a v_{dW}^{\text{corrected}}}{\sqrt{S_{dW}^{\text{corrected}}} +\varepsilon}$(如果你只是用Momentum,使用$v_{dW}$ 者修正后的$v_{dW}$,但现在我们加入了RMSprop的部分,所以我们要除以修正后$S_{dW}$ 平方根加上$\varepsilon$)。
根据类似的公式更新$b$ ,$b:=b - \frac{\alpha v_{\text{db}}^{\text{corrected}}}{\sqrt{S_{\text{db}}^{\text{corrected}}} +\varepsilon}$。
所以Adam算法结合了Momentum和RMSprop梯度下降法,并且是一种极其常用的学习算法,被证明能有效适用于不同神经网络,适用于广泛的结构。
本算法中有很多超参数,超参数学习率$a$ 重要,也经常需要调试,你可以尝试一系列值,然后看哪个有效。$\beta_{1}$ 用的缺省值为 0.9,这是 dW 的移动平均数,也就是$dW$ 加权平均数,这是Momentum涉及的项。至于超参数$\beta_{2}$,Adam论文作者,也就是Adam算法的发明者,推荐使用 0.999,这是在计算${(dW)}^{2}$ 及${(db)}^{2}$ 移动加权平均值,关于$\varepsilon$ 选择其实没那么重要,Adam论文的作者建议$\varepsilon$ $10^{-8}$,但你并不需要设置它,因为它并不会影响算法表现。但是在使用Adam的时候,人们往往使用缺省值即可,$\beta_{1}$,$\beta_{2}$ $\varepsilon$ 是如此,我觉得没人会去调整$\varepsilon$,然后尝试不同的$a$ ,看看哪个效果最好。你也可以调整$\beta_{1}$ $\beta_{2}$,但我认识的业内人士很少这么干。
为什么这个算法叫做Adam?Adam代表的是Adaptive Moment Estimation,$\beta_{1}$ 于计算这个微分($dW$),叫做第一矩,$\beta_{2}$ 来计算平方数的指数加权平均数(${(dW)}^{2}$),叫做第二矩,所以Adam的名字由此而来,但是大家都简称Adam权威算法。
顺便提一下,我有一个老朋友兼合作伙伴叫做Adam Coates。据我所知,他跟Adam算法没有任何关系,不过我觉得他偶尔会用到这个算法,不过有时有人会问我这个问题,我想你可能也有相同的疑惑。
这就是关于Adam优化算法的全部内容,有了它,你可以更加快速地训练神经网络,在结束本周课程之前,我们还要讲一下超参数调整,以及更好地理解神经网络的优化问题有哪些。下个视频中,我们将讲讲学习率衰减。
学习率衰减(Learning rate decay)
加快学习算法的一个办法就是随时间慢慢减少学习率,我们将之称为学习率衰减,我们来看看如何做到,首先通过一个例子看看,为什么要计算学习率衰减。
假设你要使用mini-batch梯度下降法,mini-batch数量不大,大概 64 或者 128 个样本,在迭代过程中会有噪音(蓝色线),下降朝向这里的最小值,但是不会精确地收敛,所以你的算法最后在附近摆动,并不会真正收敛,因为你用的$a$ 固定值,不同的mini-batch中有噪音。
但要慢慢减少学习率$a$ 话,在初期的时候,$a$ 习率还较大,你的学习还是相对较快,但随着$a$ 小,你的步伐也会变慢变小,所以最后你的曲线(绿色线)会在最小值附近的一小块区域里摆动,而不是在训练过程中,大幅度在最小值附近摆动。
所以慢慢减少$a$ 本质在于,在学习初期,你能承受较大的步伐,但当开始收敛的时候,小一些的学习率能让你步伐小一些。
你可以这样做到学习率衰减,记得一代要遍历一次数据,如果你有以下这样的训练集,
你应该拆分成不同的mini-batch,第一次遍历训练集叫做第一代。第二次就是第二代,依此类推,你可以将$a$ 习率设为$a= \frac{1}{1 + decayrate * \text{epoch}\text{-num}}a_{0}$(decay-rate称为衰减率,epoch-num为代数,$\alpha_{0}$ 初始学习率),注意这个衰减率是另一个你需要调整的超参数。
这里有一个具体例子,如果你计算了几代,也就是遍历了几次,如果$a_{0}$ 0.2,衰减率decay-rate为 1,那么在第一代中,$a = \frac{1}{1 + 1}a_{0} = 0.1$,这是在代入这个公式计算($a= \frac{1}{1 + decayrate * \text{epoch}\text{-num}}a_{0}$),此时衰减率是 1 而代数是 1。在第二代学习率为 0.67,第三代变成 0.5,第四代为 0.4 等等,你可以自己多计算几个数据。要理解,作为代数函数,根据上述公式,你的学习率呈递减趋势。如果你想用学习率衰减,要做的是要去尝试不同的值,包括超参数$a_{0}$,以及超参数衰退率,找到合适的值,除了这个学习率衰减的公式,人们还会用其它的公式。
比如,这个叫做指数衰减,其中$a$ 当于一个小于 1 的值,如$a ={0.95}^{\text{epoch-num}} a_{0}$,所以你的学习率呈指数下降。
人们用到的其它公式有$a =\frac{k}{\sqrt{\text{epoch-num}}}a_{0}$ 者$a =\frac{k}{\sqrt{t}}a_{0}$($t$ mini-batch的数字)。
有时人们也会用一个离散下降的学习率,也就是某个步骤有某个学习率,一会之后,学习率减少了一半,一会儿减少一半,一会儿又一半,这就是离散下降(discrete stair cease)的意思。
到现在,我们讲了一些公式,看学习率$a$ 竟如何随时间变化。人们有时候还会做一件事,手动衰减。如果你一次只训练一个模型,如果你要花上数小时或数天来训练,有些人的确会这么做,看看自己的模型训练,耗上数日,然后他们觉得,学习速率变慢了,我把$a$ 小一点。手动控制$a$ 然有用,时复一时,日复一日地手动调整$a$,只有模型数量小的时候有用,但有时候人们也会这么做。
所以现在你有了多个选择来控制学习率$a$。你可能会想,好多超参数,究竟我应该做哪一个选择,我觉得,现在担心为时过早。下一周,我们会讲到,如何系统选择超参数。对我而言,学习率衰减并不是我尝试的要点,设定一个固定的$a$,然后好好调整,会有很大的影响,学习率衰减的确大有裨益,有时候可以加快训练,但它并不是我会率先尝试的内容,但下周我们将涉及超参数调整,你能学到更多系统的办法来管理所有的超参数,以及如何高效搜索超参数。
这就是学习率衰减,最后我还要讲讲神经网络中的局部最优以及鞍点,所以能更好理解在训练神经网络过程中,你的算法正在解决的优化问题,下个视频我们就好好聊聊这些问题。









































































