我正在尝试用C ++编写一个简单的梯度下降算法(10,000次迭代)。这是我的计划:
#include<iostream>
#include<cmath>
using namespace std;
int main(){
double learnrate=10;
double x=10.0; //initial start value
for(int h=1; h<=10000; h++){
x=x-learnrate*(2*x + 100*cos(100*x));
}
cout<<"The minimum is at y = "<<x*x + sin(100*x)<<" and at x = "<<x;
return 0;
}
输出最终为:y = nan,x = nan。我试着通过将它们放入文件中来查看x和y的值,经过一定的迭代后,我得到所有的nans(对于x和y)。编辑:我选择学习率(或步长)为10作为实验,之后我将使用更小的值。
答案 0 :(得分:1)
您的配方一定有问题。 x的前10个值已经像地狱一样增加:
-752.379
15290.7
-290852
5.52555e+06
-1.04984e+08
1.9947e+09
-3.78994e+10
7.20088e+11
-1.36817e+13
2.59952e+14
无论您选择什么起始值,下一个x
的绝对值都会更大。
|next_x| = | x - 20 * x - 100 * cos(100*x) |
例如,考虑选择一个非常小的起始值(|x|->0
),然后
|next_x| = | 0 - 20 * 0 - 100 * cos ( 0 ) | = 100
答案 1 :(得分:0)
在调用余弦函数之前打印x
,您会看到在NaN
(h = 240
)之前打印的最后一个数字是:
-1.7761e + 307
这意味着该值将变为无穷大,无法表示(因此不是数字)。
它溢出double
类型。
如果你使用long double
,你将在1000次迭代中成功,但你仍然会以10000次迭代溢出该类型。
所以问题是参数learnrate
太大了。如上所述,您应该在使用范围较大的数据类型时执行步骤。
答案 2 :(得分:0)
因为在h = 240时变量&#34; x&#34;超过双重类型的限制(1.79769e + 308)。这是一个不同的算术级数。你需要降低你的学习率。
还有几件事: 1-不要使用&#34;使用命名空间std;&#34;这是不好的做法。 2-您可以使用&#34; std :: isnan()函数来识别这种情况。
以下是一个例子:
#include <iomanip>
#include <limits>
int main()
{
double learnrate = 10.0;
double x = 10.0; //initial start value
std::cout<<"double type maximum=" << std::numeric_limits<double>::max()<<std::endl;
bool failed = false;
for (int h = 1; h <= 10000; h++)
{
x = x - learnrate*(2.0*x + 100.0 * std::cos(100.0 * x));
if (std::isnan(x))
{
failed = true;
std::cout << " Nan detected at h=" << h << std::endl;
break;
}
}
if(!failed)
std::cout << "The minimum is at y = " << x*x + std::sin(100.0*x) << " and at x = " << x;
return 0;
}
答案 3 :(得分:0)
“学习率”太高了。例如,将其更改为1e-4,程序可以工作,初始值至少为10。当学习率为10时,迭代跳过解决方案太远了。
最好的情况是,梯度下降不是一个好的算法。对于严肃的应用程序,您希望使用更好的东西好多了。搜索布伦特优化器和BFGS。