# Домашнє завдання · Зворотне поширення помилки

> Лекція: [lecture.html](lecture.html) · Практика: [practice.ipynb](practice.ipynb) · Тест: [quiz.html](quiz.html)

Мета цієї роботи одна: навчитись **доводити**, що градієнт порахований правильно, а не
сподіватись на це. Усі три рівні спираються на ту саму мережу 2 → 2 → 1 з лекції.

---

## 🟢 Рівень 1 — База

Повтори повний ручний розрахунок з [таблиці 1 лекції](lecture.html#s5) на **інших числах**.
Візьми те саме оголошення (`x₁ = −1.00`, `x₂ = −1.50`), але зміни ваги другого шару:
`W⁽²⁾ = [0.90, −0.50]`, `b⁽²⁾ = −0.30`. Ваги першого шару лиши як були.

Порахуй олівцем (або в зошиті) і випиши: `z⁽¹⁾`, `a⁽¹⁾`, `z⁽²⁾`, `ŷ`, `L`, `δ⁽²⁾`,
`δ⁽¹⁾` і всі девʼять градієнтів. Потім підстав ці ваги в `practice.ipynb` і звір.

**Зроблено, якщо:** твої числа збігаються з тим, що надрукував зошит, до чотирьох знаків
після коми, а чисельна перевірка градієнта дала відносну похибку менше `1e-7`. Окремо
поясни одним реченням, чому `δ⁽²⁾` цього разу вийшов іншим за знаком або за модулем, ніж
у лекції.

---

## 🟡 Рівень 2 — Плюс

Заміни в прихованому шарі `tanh` на **сигмоїду**. Її похідна дорівнює `a·(1 − a)`, тобто
не перевищує 0.25 ніде — це рівно вчетверо менше за максимум похідної `tanh`.

Що треба зробити:

1. переписати `прямий_прохід` і `зворотний_прохід` під нову активацію;
2. прогнати чисельну перевірку градієнта на ручному прикладі;
3. навчити мережу на дошці оголошень з тією самою швидкістю навчання й тією самою кількістю
   епох, що й у практиці;
4. побудувати обидві криві втрат на одному графіку.

**Зроблено, якщо:** перевірка градієнта пройдена (відносна похибка < `1e-7`), обидві криві
втрат намальовані, і ти написав два-три речення про те, **яка версія навчається швидше й
чому** — з посиланням на конкретне число: максимум похідної активації.

---

## 🔴 Рівень 3 — Виклик

Напиши **власну перевірку градієнта як функцію**, яку можна натравити на будь-яку мережу:

```python
def перевірити_градієнт(ваги, функція_втрат, функція_градієнта, X, y, крок=1e-5):
    """Повертає відносну похибку й імена параметрів, у яких вона найбільша."""
```

Далі зроби три речі:

1. додай мережі другий прихований шар (`2 → 4 → 4 → 1`) і переконайся, що твоя функція
   проходить на всіх параметрах;
2. навмисно введи **три різні помилки** — забуте транспонування, переплутаний знак дельти,
   множення на похідну не того шару — і покажи, яку відносну похибку дає кожна;
3. знайди помилку, яку твоя перевірка **не ловить**. Підказка: подумай, що станеться,
   якщо помилка є і в `зворотному_проході`, і в `функції_втрат` однаково.

**Зроблено, якщо:** функція працює на мережі довільної глибини, для трьох навмисних помилок
виписані їхні відносні похибки (усі більші за `1e-2`), і описано щонайменше один тип помилки,
який чисельна перевірка пропускає, — з поясненням чому.

---

## Підказки

- **Рівень 1.** Знак `δ⁽²⁾` залежить тільки від того, з якого боку від правильної відповіді
  опинився прогноз. Порахуй спершу `ŷ`, і знак стане очевидним ще до всіх похідних.
- **Рівень 2.** Похідну сигмоїди зручно рахувати не з `z`, а з уже готового `a`: прямий
  прохід його вже порахував, і другий раз експоненту брати не треба.
- **Рівень 3.** Не пиши перевірку через цикли по матрицях руками — склади всі параметри
  в один плаский вектор, зроби перевірку на ньому, а потім розклади назад. Тоді код не
  залежатиме від кількості шарів.
- Якщо чисельна перевірка дає похибку близько `1e-3` і ти не бачиш помилки в коді —
  спершу перевір крок `h`. Половина «багів» на цьому місці виявляється неправильним `h`.
