为什么我一直把Nan作为输出?

时间:2017-09-29 20:10:47

标签: c++ math

我正在尝试用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作为实验,之后我将使用更小的值。

4 个答案:

答案 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,您会看到在NaNh = 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。