В этом примере показано, как использовать оптимизацию арендованного квадрата или градиентный спуск для выполнения линейной регрессии с использованием C ++.

Здесь также показано, как вычислить истинное значение и сравнить результат оптимизации с достоверным показателем.

Из-за своей простоты эту проблему часто задают во время собеседований, связанных с машинным обучением. Понимание его реализации необходимо для построения более сложных регрессионных моделей, таких как логистическая регрессия.

Проблема: для заданных векторов x и y (x - вход, y - метка). Найдите оптимальную линию, которая наилучшим образом предсказывает будущее y с будущим x.

Нам нужно найти красную линию: y = ax + b, которая лучше всего соответствует шаблону точек данных.

Мы определяем функцию стоимости как среднеквадратичную ошибку:

C = Сумма (y_i - (a * x_i + b)) ² / N

Чтобы вычислить градиентный спуск, нам нужно вычислить частные производные от C по наклону a и точке пересечения b.

dC / da = 2 / N * Sum (-x_i * y_i + a * x_i² + b * x_i)

dC / db = 2 / N * Сумма (-y_i + a * x_i + b)

После того, как мы получим da и db, мы можем обновлять наклон a и перехватывать b итеративно с небольшой скоростью обучения.

slope = slope - скорость обучения * да

перехват = перехват - скорость обучения * db

Мы применяем вышеуказанное итеративное обновление до тех пор, пока либо изменение ошибки не станет очень маленьким, либо da / db не станет очень маленьким (больше нет градиента).

Обратите внимание, что нам не нужно запускать каждую итерацию со всеми данными обучения. Если размер обучающих данных очень велик, мы можем запускать каждую итерацию только с пакетом данных (подмножеством обучающих данных).

Чтобы вычислить истинное значение угла наклона и точки пересечения, нам необходимо выполнить следующий расчет для всех данных обучения.

наклон = (N * Сумма (x_i * y_i) -Сумма (x_i) * Сумма (y_i)) / (N * Сумма (x_i²) -Сумма (x_i) ²)

перехват = (Сумма (y_i) -a * Сумма (x_i)) / N

Пример реализации находится на C ++

двойной getSlope (вектор ‹двойной› & x, вектор ‹двойной› & y)

{

двойной sx = накопить (x.begin (), x.end (), 0);

double sy = накопить (y.begin (), y.end (), 0);

double sxx = внутренний_продукт (x.begin (), x.end (), x.begin (), 0);

двойной sxy = внутренний_продукт (x.begin (), x.end (), y.begin (), 0);

int n = static_castint› (x.size ());

// (n * sxy - sx * sy) / (n * sxx - sx * sx)

двойной nor = n * sxy - sx * sy;

двойной denor = n * sxx - sx * sx;

если (денор! = 0)

{

возврат nor / denor;

}

return numeric_limits ‹double› :: max ();

}

double getIntercept (vector ‹double› & x, vector ‹double› & y, двойной наклон)

{

двойной sx = накопить (x.begin (), x.end (), 0);

double sy = накопить (y.begin (), y.end (), 0);

int n = static_castint› (x.size ());

return (sy-slope * sx) / n;

}

// наклон: а

// перехват: b

// производная от наклона: da

// производная от перехвата: db

double getCost (vector ‹double› & x, vector ‹double› & y, double a, double b, двойной и da, двойной и db)

{

int n = static_castint› (x.size ());

двойной sx = накопить (x.begin (), x.end (), 0);

double sy = накопить (y.begin (), y.end (), 0);

double sxx = внутренний_продукт (x.begin (), x.end (), x.begin (), 0);

двойной sxy = внутренний_продукт (x.begin (), x.end (), y.begin (), 0);

double syy = inner_product (y.begin (), y.end (), y.begin (), 0);

double cost = syy - 2 * a * sxy - 2 * b * sy + a * a * sxx + 2 * a * b * sx + n * b * b;

стоимость / = n;

da = 2 * (- sxy + a * sxx + b * sx) / n;

db = 2*(-sy + a*sx + n*b)/n;

возврат стоимости;

}

void linearRegression (vector ‹double› & x, vector ‹double› & y, двойной наклон = 1, двойной перехват = 0)

{

двойная скорость = 0,0002;

двойной порог = 0,0001;

int iter = 0;

в то время как (верно)

{

двойной da = 0;

двойной db = 0;

double cost = getCost (x, y, slope, intercept, da, db);

если (iter% 1000 == 0)

{

cout ‹---------------- ”Iter:« ‹* iter ‹* “cost =« ‹< cost ‹…… «da =« ‹< da ‹……………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………

}

iter ++;

если (абс (да) ‹порог && абс (дб)‹ порог)

{

cout ‹---------------- ”y =« ‹* slope ‹

перерыв;

}

наклон - = lrate * da;

перехват - = lrate * db;

}

}

int main () {

вектор ‹double› x = {71, 73, 64, 65, 61, 70, 65, 72, 63, 67, 64};

вектор ‹двойной› y = {160, 183, 154, 168, 159, 180, 145, 210, 132, 168, 141};

// инициализируем случайными двумя точками

cout ‹---------------- «Инициализация случайными 2 точками» ‹< endl;

вектор ‹double› xSub = {71, 73};

вектор ‹двойной› ySub = {160, 183};

двойной slopeSub = getSlope (xSub, ySub);

двойной interceptSub = getIntercept (xSub, ySub, slopeSub);

cout ‹* ”y =« ‹* slopeSub ‹* »* x +« ‹* interceptSub ‹* endl;

linearRegression (x, y, slopeSub, interceptSub);

cout ‹

двойной наклон = getSlope (x, y);

двойной перехват = getIntercept (x, y, наклон);

cout ‹---------------- ”y =« ‹* slope ‹

возврат 0;

}