Иногда обучение идёт отвратительно при совершенно правильном коде, и причина не в ошибке, а в форме самой задачи.
Если по разным направлениям ошибка растёт с сильно разной скоростью, линии равной ошибки вытягиваются в узкую долину — овраг.
Шаг спуска считается так: от веса отнимают наклон, умноженный на скорость обучения. Множитель этот один на все направления, поэтому брать его приходится таким, чтобы не разойтись по самому крутому направлению — и вдоль долины спуск тогда ползёт. Овраг — первое, что проверяют, когда сеть не учится, а ошибок в коде нет.
Разберём его на функции двух переменных, где вытянутость задаётся одним числом и её видно сразу.
Подробно: Все веса сразу.
Задание
Возьмём функцию двух переменных L(x, y) = a·x² + y². Производные: 2·a·x по x и 2·y по y.
Напишите функцию
def descent2d(a, lr, steps):
...
Функция стартует из точки x = 1, y = 1 и возвращает список пар (x, y) после каждого шага, включая начальную: при steps = 30 в списке 31 пара.
Благодаря параметру a оба случая — овраг и круглая чаша — это два вызова одной функции.
Как проверить себя
Вызовите descent2d(100, 0.009, 30) и descent2d(1, 0.009, 30) и сравните поведение координат. В первом случае x на первых шагах перескакивает через ноль туда-обратно, а y почти не двигается; во втором обе координаты ведут себя одинаково. Посчитайте множители 1 − 2·a·lr и 1 − 2·lr — они объясняют и то и другое.
Значение lr = 0.009 выбрано не случайно. Попробуйте 0.005, 0.01 и 0.02 и объясните каждый случай через множитель.
Числа сравниваются с допуском, а не как строки: достаточно совпадения в первых четырёх знаках. Печатать ничего не надо — функция возвращает значение.