梯度下降返回 nan
Gradient descent returning nan
我需要编写一个函数来获得数据集的曲线拟合。下面的代码是我所拥有的。它尝试使用梯度下降来找到最适合数据的多项式系数。
//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。
有人知道我做错了什么吗?
我尝试了 degree = 2, iteration = 10
并得到了 nan
以外的结果(值大约几千)向 iteration
添加一个似乎使结果的大小在那之后增加了大约 3 倍.
根据这个观察,我猜测结果乘以 count
。
表达式中
coeffs[j] -= (1/count) * sums[j] * learningRate;
1
和count
都是整数,所以整数除法在1/count
中完成,如果count
大于 1.
除此之外,您可以将乘法结果除以 count
。
coeffs[j] -= sums[j] * learningRate / count;
另一种方法是使用 1.0
(double
值)而不是 1
。
coeffs[j] -= (1.0/count) * sums[j] * learningRate;
旁白:
候选 NAN
来源正在添加相反符号的值,其中 1 是无穷大。鉴于 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()
正文。
我需要编写一个函数来获得数据集的曲线拟合。下面的代码是我所拥有的。它尝试使用梯度下降来找到最适合数据的多项式系数。
//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。
有人知道我做错了什么吗?
我尝试了 degree = 2, iteration = 10
并得到了 nan
以外的结果(值大约几千)向 iteration
添加一个似乎使结果的大小在那之后增加了大约 3 倍.
根据这个观察,我猜测结果乘以 count
。
表达式中
coeffs[j] -= (1/count) * sums[j] * learningRate;
1
和count
都是整数,所以整数除法在1/count
中完成,如果count
大于 1.
除此之外,您可以将乘法结果除以 count
。
coeffs[j] -= sums[j] * learningRate / count;
另一种方法是使用 1.0
(double
值)而不是 1
。
coeffs[j] -= (1.0/count) * sums[j] * learningRate;
旁白:
候选 NAN
来源正在添加相反符号的值,其中 1 是无穷大。鉴于 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()
正文。