随机梯度下降算法(SGD)是最基本的梯度下降算法,对比BGD算法,SGD算法由于不是基于全部的数据,而是在每轮迭代中,随机优化某一条数据上的损失函数使更新速度大大加快。但其准确度会下降。Adam算法是基于AdaGrad算法和RMSProp算法,可以结合动量和自适应学习的集大成者,是目前应用最广泛的算法。Adamw算法效果与Adam相同,但效率更高,因为Adamw直接将正项的梯度加入反响传播的公式中年,省去了手动输入。我们通过计算接近x=6上y=(sin(x))^2+cos(x)+5y=(sin(x))2+cos(x)+5极小值点所需要迭代的次数,来对比SGD、Adam、Adamw算法的优缺点。下列就是有关SGD、Adam、Adamw算法的控制变量对比实验:
SGD
为了求得方程y=(sin(x))^2+cos(x)+5y=(sin(x))2+cos(x)+5的极小值点,我们可以通过计算此点的梯度,再求得靠近此点的梯度,梯度变化较小的点就是我们所需要求得的极值点。下面是用SGD代码实现的:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
double x = 6;
double lr = 1e-2;//学习效率
double error = 1e-6;//误差范围
double grad = 2 * sin(x) * cos(x) - sin(x);//梯度
int step=0;//循环次数
while(1){
if(fabs(grad)>error)
{
step++;
x = x - grad * lr;
grad = 2 * sin(x) * cos(x) - sin(x);
printf(" %d %lf %lf\n", step,x, sin(x) * sin(x) + cos(x) + 5);
}
else{
return 0;
}
}
SGD代码实现的结果:
./SGD
1174 6.283183 6.000000
1175 6.283183 6.000000
1176 6.283183 6.000000
1177 6.283183 6.000000
1178 6.283183 6.000000
1179 6.283183 6.000000
1180 6.283183 6.000000
1181 6.283183 6.000000
1182 6.283183 6.000000
1183 6.283183 6.000000
1184 6.283183 6.000000
1185 6.283183 6.000000
1186 6.283183 6.000000
1187 6.283183 6.000000
1188 6.283183 6.000000
1189 6.283183 6.000000
1190 6.283183 6.000000
1191 6.283183 6.000000
1192 6.283183 6.000000
1193 6.283183 6.000000
1194 6.283183 6.000000
1195 6.283183 6.000000
1196 6.283184 6.000000
1197 6.283184 6.000000
1198 6.283184 6.000000
1199 6.283184 6.000000
由此可以看出,SGD算法实现求得函数的极小值需要迭代多次。
Adam
同SGD算法一样,Adam算法也需要通过计算所在x处的梯度变化来判断极小值点的坐标,与SGD算法不同的是,Adam算法结合了AdaGrad算法和RMSProp算法,其准确率和迭代次数大大提升。通过设置相同的学习效率和误差范围,我们可以清楚的比较Adam算法和SGD算法,下面是用Adam算法实现的:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
void main(){
double x=6,alpha=1e-3;//学习效率
double grad=2*sin(x)*cos(x)-sin(x);//梯度
double v=80;//动量
double error=0.9e-3;//误差范围
double r=80;//控制学习率
double p1=0.99,p2=0.999;
int step=0;//循环次数
while(fabs(grad)>error){
step++;
grad=2*sin(x)*cos(x)-sin(x);
v=p1*v+(1-p1)*grad;
r=p2*r+(1-p2)*grad*grad;
v=v/(1-pow(p1,step));
r=r/(1-pow(p2,step));
x=x-((alpha*v)/(sqrt(r)+1e-7))*grad;
printf("%d %lf %lf\n",step,x,sin(x)*sin(x)+cos(x)+5);
}
}
Adam算法运行结果:
./Adam
1 6.007204 6.036410
2 6.022906 6.032547
3 6.050224 6.026283
4 6.089712 6.018310
5 6.137491 6.010482
6 6.185524 6.004742
7 6.225285 6.001673
8 6.252467 6.000472
9 6.268149 6.000113
10 6.276089 6.000025
11 6.279800 6.000006
12 6.281486 6.000001
13 6.282264 6.000000
14 6.282640 6.000000
15 6.282832 6.000000
由此可见,Adam算法的效率相比SGD大大提升,其迭代次数大大减少,是当今运用最为广泛的算法。
Adamw
Adamw算法和是基于Adam算法进行改进的算法。其在计算梯度的时候会加上对正则项求梯度的结果,不需要手动的输入,因而效率相比Adam更高。通过控制与Adam算法相同的参数学习效率,误差范围,动量以及控制学习率,通过不断的改变schedule multiplier,找到最优解,我们可以清楚的看到Adamw算法的优势所在,下面是用Adamw实现的:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
void main(){
double learning_rate=1e-3;//学习效率
double p1=0.99,p2=0.999;
double x=6;
double grad=2*sin(x)*cos(x)-sin(x);// 梯度
double v=80,r=80;
double error=0.9e-3;//误差范围
int step=0;//循环次数
double n=0.0094;//schedule multiplier
while(fabs(grad)>error){
step++;
grad=2*sin(x)*cos(x)-sin(x)+x*n;
v=p1*v+(1-p1)*grad;
r=p2*r+(1-p2)*grad*grad;
v=v/(1-pow(p1,step));
r=r/(1-pow(p2,step));
x=x-n*(learning_rate*v/sqrt(r)+1e-7)+x*n;
printf("%d %lf %lf\n",step,x,sin(x)*sin(x)+cos(x)+5);
}
}
Adamw算法运行的结果:
./Adamw
1 6.056137 6.025006
2 6.112478 6.014324
3 6.168866 6.006485
4 6.225153 6.001681
5 6.281243 6.000002
由此可见,通过调节适合的schedule multiplier,Adamw算法所需要的迭代次数相比Adam所需要的次数更少,效率更高。