# Пример 1. Траектория параметра

#### Цель

Понять, как именно параметр модели движется во время обучения.

Не итоговый коэффициент, не качество прогноза, а сам процесс: как меняется значение $$w$$ от эпохи к эпохе, как ведет себя градиент и как уменьшается ошибка.

Этот пример нужен для того, чтобы формула

$$
w = w - \eta \frac{dL}{dw}
$$

перестала быть абстрактной записью и превратилась в наблюдаемый процесс.

#### Сценарий

Мы намеренно убираем любую предметную область. Нас интересует только динамика обучения. Берем максимально простой набор данных:

```
x = [1, 2, 3, 4]
y = [2, 4, 6, 8]
```

Здесь зависимость идеальная: $$y = 2x$$.

Правильное значение параметра $$w = 2$$.

Модель предельно простая:

$$
\hat{y} = w \cdot  x
$$

Функция ошибки – среднеквадратичная ([MSE](https://apphp.gitbook.io/ai-for-php-developers/vvedenie/glossarii#mse-mean-squared-error)). Метод оптимизации – [batch gradient descent](https://apphp.gitbook.io/ai-for-php-developers/vvedenie/glossarii#batch-gradient-descent-bgd).

Начинаем обучение с $$w = 0$$.

#### Реализация на PHP

```php
$x = [1, 2, 3, 4];
$y = [2, 4, 6, 8]; // идеальная зависимость y = 2x

$w = 0.0;
$learningRate = 0.1;
$n = count($x);

echo "epoch\tw\tgradient\tloss\n";

for ($epoch = 1; $epoch <= 20; $epoch++) {

    $gradient = 0.0;
    $loss = 0.0;

    for ($i = 0; $i < $n; $i++) {
        $pred = $w * $x[$i];
        $error = $pred - $y[$i];

        $loss += $error ** 2;
        $gradient += $x[$i] * $error;
    }

    $loss /= $n;
    $gradient = (2 / $n) * $gradient;

    echo $epoch . "\t" .
         round($w, 4) . "\t" .
         round($gradient, 4) . "\t\t" .
         round($loss, 4) . "\n";

    $w -= $learningRate * $gradient;
}

// Результат: 
// epoch	w		gradient	    loss
// 1	    0		     -30		    30
// 2	    3		      15		   7.5
// 3	  1.5		    -7.5		 1.875
// ...
// 20	    2		       0		     0
```

#### Что происходит математически

Ошибка:

$$
L(w) = \frac{1}{n} \sum (w \cdot x\_i - y\_i)^2
$$

Производная:

$$
\frac{dL}{dw} = \frac{2}{n} \sum x\_i (w \cdot x\_i - y\_i)
$$

Каждая эпоха делает одно и то же:

1. Считает текущую ошибку
2. Считает градиент
3. Сдвигает параметр в сторону уменьшения ошибки

В нашем конкретном наборе данных можно упростить выражение.\
Поскольку $$y\_i=2x\_iy\_i$$​, производная принимает вид:

$$
\frac{dL}{dw} = 15 (w - 2)
$$

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

#### Что важно увидеть глазами

Когда вы запускаете код, вы видите таблицу:

* эпоха
* текущее значение $$w$$
* значение градиента
* значение ошибки

И именно здесь начинается настоящее понимание.

Во-первых, пока $$w < 2$$, градиент отрицательный.&#x20;

Это означает, что формула обновления

$$
w = w - η∇
$$

увеличивает $$w$$. Параметр движется вправо – к минимуму.

Во-вторых, по мере приближения к $$w = 2$$ величина градиента уменьшается. Шаги становятся короче. Движение замедляется. Это естественно: склон становится пологим.

В нашем примере при `learning rate` = 0.1 параметр уже на первой эпохе перелетает через 2. После этого знак градиента меняется, и начинается движение в обратную сторону. Возникают затухающие колебания вокруг минимума, которые постепенно уменьшаются по мере снижения величины градиента.

Именно это и есть реальная работа производной: она не говорит, где минимум. Она говорит, куда двигаться сейчас.

#### Интерпретация как движения по ландшафту

Можно представить себе график ошибки как чашу (перевёрнутый холм).

* Пока мы на левом склоне, наклон отрицательный, поэтому шаг делается вправо
* В точке минимума наклон равен нулю
* На правом склоне наклон положительный, поэтому шаг делается влево

Градиент – это локальный наклон поверхности. Обновление параметра – это шаг вниз по этому наклону. Каждая строка вывода в консоли – это один шаг по поверхности ошибки.

#### Выводы

Этот пример показывает несколько фундаментальных вещей.

Во-первых, градиент – это направление движения, а не ответ. Он не сообщает нам минимум напрямую, он лишь указывает локальный склон.

Во-вторых, уменьшение шага происходит естественно. Никакой дополнительной логики не требуется – градиент сам уменьшается возле минимума.

В-третьих, обучение – это не мгновенный расчет, а последовательность маленьких коррекций.

И самое главное: формула обновления параметра перестает быть формальным символом из учебника. Она становится реальным процессом изменения числа от эпохи к эпохе.

После этого примера градиентный спуск становится более понятным. Это просто движение по поверхности ошибки – шаг за шагом.

{% hint style="info" %}
Чтобы самостоятельно протестировать этот код, воспользуйтесь [онлайн-демонстрацией](https://aiwithphp.org/books/ai-for-php-developers/examples/part-2/gradient-descent-on-fingers) для его запуска.
{% endhint %}
