【问题标题】:Gradient descent returning nan梯度下降归南
【发布时间】:2020-11-28 08:24:32
【问题描述】:

我需要编写一个函数来获得数据集的曲线拟合。下面的代码是我所拥有的。它尝试使用梯度下降来找到最适合数据的多项式系数。

//solves for y using the form y = a + bx + cx^2 ...
double calc_polynomial(int degree, double x, double* coeffs) {
    double y = 0;

    for (int i = 0; i <= degree; i++)
        y += coeffs[i] * pow(x, i);

    return y;
}

//find polynomial fit
//returns an array of coefficients degree + 1 long
double* poly_fit(double* x, double* y, int count, int degree, double learningRate, int iterations) {
    double* coeffs = malloc(sizeof(double) * (degree + 1));
    double* sums = malloc(sizeof(double) * (degree + 1));

    for (int i = 0; i <= degree; i++)
        coeffs[i] = 0;

    for (int i = 0; i < iterations; i++) {
        //reset sums each iteration
        for (int j = 0; j <= degree; j++)
            sums[j] = 0;

        //update weights
        for (int j = 0; j < count; j++) {
            double error = calc_polynomial(degree, x[j], coeffs) - y[j];

            //update sums
            for (int k = 0; k <= degree; k++)
                sums[k] += error * pow(x[j], k);
        }

        //subtract sums
        for (int j = 0; j <= degree; j++)
            coeffs[j] -= sums[j] * learningRate;
    }

    free(sums);

    return coeffs;
}

还有我的测试代码:

double x[] = { 0, 1, 2, 3, 4 };
double y[] = { 5, 3, 2, 3, 5 };
int size = sizeof(x) / sizeof(*x);

int degree = 1;
double* coeffs = poly_fit(x, y, size, degree, 0.01, 1000);

for (int i = 0; i <= degree; i++)
    printf("%lf\n", coeffs[i]);

上面的代码在 degree = 1 时有效,但任何更高的值都会导致系数返回为 nan。

我也试过替换

coeffs[j] -= sums[j] * learningRate;

coeffs[j] -= (1/count) * sums[j] * learningRate;

但后来我返回 0 而不是 nan。

有人知道我做错了什么吗?

【问题讨论】:

  • 1/count整数除法,其结果被截断。
  • 啊,这就是问题所在……我现在感觉很笨
  • 我尝试了degree = 2, iteration = 10 并得到了除nan 以外的结果(值约为几千)在iteration 上加一似乎使结果的幅度在此之后增大了大约3 倍。跨度>
  • 考虑避免重复调用 pow 实现 Horner's method 来评估多项式和类似的错误总和。
  • 注意:使用printf("%le\n", coeffs[i]);printf("%lf\n", coeffs[i]);提供更多信息

标签: c machine-learning gradient-descent


【解决方案1】:

我尝试了degree = 2, iteration = 10 并得到了除nan 以外的结果(值约为几千)在iteration 上加一似乎会使结果的幅度在此之后增大约 3 倍。

根据这个观察,我猜结果是乘以count

在表达式中

coeffs[j] -= (1/count) * sums[j] * learningRate;

1count都是整数,所以整数除法1/count中完成,如果count大于1则归零。

除此之外,您可以将乘法的结果除以count

coeffs[j] -= sums[j] * learningRate / count;

另一种方法是使用1.0double 值)代替1

coeffs[j] -= (1.0/count) * sums[j] * learningRate;

【讨论】:

    【解决方案2】:

    旁白:

    候选NAN 源正在添加相反的有符号值,其中一个是无穷大。鉴于 OP 正在使用快速增长的 pow(x, k),使用其他技术帮助。

    考虑一个链式乘法而不是pow()。结果通常在数值上更稳定。 calc_polynomial() 例如:

    double calc_polynomial(int degree, double x, double* coeffs) {
      double y = 0;
      // for (int i = 0; i <= degree; i++)
      for (int i = degree; i >= 0; i--)
        //y += coeffs[i] * pow(x, i);
        y = y*x + coeffs[i];
      }
      return y;
    }
    

    类似的代码可用于main() 正文。

    【讨论】:

      猜你喜欢
      • 2021-12-22
      • 2021-04-30
      • 1970-01-01
      • 1970-01-01
      • 2021-06-23
      • 2021-02-20
      • 1970-01-01
      • 2016-06-13
      相关资源
      最近更新 更多