#c #machine-learning #gradient-descent
#c #машинное обучение #градиентный спуск
Вопрос:
Мне нужно написать функцию, чтобы получить соответствие кривой набора данных. Приведенный ниже код — это то, что у меня есть. Он пытается использовать градиентный спуск для нахождения полиномиальных коэффициентов, которые наилучшим образом соответствуют данным.
//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("%lfn", coeffs[i]);
Приведенный выше код работает, когда степень = 1, но все, что выше, приводит к тому, что коэффициенты возвращаются как nan.
Я также пытался заменить
coeffs[j] -= sums[j] * learningRate;
с
coeffs[j] -= (1/count) * sums[j] * learningRate;
но тогда я получаю обратно 0s вместо nan.
Кто-нибудь знает, что я делаю не так?
Комментарии:
1.
1/count
является целочисленным делением , результат которого усекается.2. Ах, это была проблема… Теперь я чувствую себя глупо
3. Я попробовал
degree = 2, iteration = 10
и получил результаты, отличные отnan
(значения около нескольких тысяч) Добавление единицы кiteration
, кажется, увеличивает величину результатов примерно в 3 раза после этого.4. Подумайте о том, чтобы избежать повторных вызовов для
pow
реализации метода Хорнера для вычисления многочлена и чего-то подобного для суммы ошибок.5. Примечание: использование
printf("%len", coeffs[i]);
более информативно, чемprintf("%lfn", coeffs[i]);
Ответ №1:
Я попробовал 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;
Ответ №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()
тела.