[英]Why do I keep getting Nan as output?
我正在尝试用 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 的值放入一个文件来查看它们,经过一定量的迭代后,我得到了所有的 nan(对于 x 和 y)。 编辑:我将学习率(或步长)选为 10 作为实验,之后我将使用更小的值。
你的公式一定有问题。 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
在调用余弦函数之前打印x
,您将看到在NaN
(在h = 240
)之前打印的最后一个数字是:
-1.7761e+307
这意味着该值将趋于无穷大,无法表示(因此不是数字)。
它溢出了double
类型。
如果您使用long double
,您将在 1000 次迭代中成功,但您仍然会在 10000 次迭代中溢出类型。
所以问题是参数learnrate
太大了。 正如我上面建议的那样,您应该在使用范围更大的数据类型时执行 let 步骤。
因为在 h=240 时变量 "x" 超出了双精度类型的限制 (1.79769e+308)。 这是一个发散的等差数列。 您需要降低学习率。
还有几件事: 1- 不要使用“使用命名空间 std;” 这是不好的做法。 2- 您可以使用“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;
}
“学习率”太高了。 例如,将其更改为 1e-4,程序就可以运行,初始值至少为 10。 当学习率为 10 时,迭代跳得太远超过解决方案。
在最好的情况下,梯度下降不是一个好的算法。 对于严肃的应用程序,您希望使用更好的东西。 好多了。 搜索 Brent 优化器和 BFGS。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.