我需要编写一个函数来拟合数据集的曲线。下面的代码是我目前的尝试。它试图使用梯度下降法来找到最适合数据的多项式系数。
//使用y = a + bx + cx^2 ...的形式计算y
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;
}
//查找多项式拟合
//返回一个长度为degree + 1的系数数组
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++) {
//每次迭代重置sums
for (int j = 0; j <= degree; j++)
sums[j] = 0;
//更新权重
for (int j = 0; j < count; j++) {
double error = calc_polynomial(degree, x[j], coeffs) - y[j];
//更新sums
for (int k = 0; k <= degree; k++)
sums[k] += error * pow(x[j], k);
}
//减去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时,上述代码可以正常工作,但任何更高的degree都会导致系数返回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;