Митасов Андрей Павлович : другие произведения.

Металоция неведомого. Модуль Т. Тренинг

Самиздат: [Регистрация] [Найти] [Рейтинги] [Обсуждения] [Новинки] [Обзоры] [Помощь|Техвопросы]
Ссылки:


 Ваша оценка:
  • Аннотация:
    Выжимка информации из Инета по глубокому обучению нейросетей.
    Предварительный анализ.
    Крайнее изменение 28.04.2024

  
  "Мы все учились понемногу
  Чему-нибудь и как-нибудь..."
  
  Александр сергеевич Пушкин - "Евгений Онегин".
  
  Оглавление:
   Вместо предисловия.
   Начинаем с "азов".
   "Повторение мать учения".
   Погружение в математику "обратного распространения".
   "Звонок другу".
   Маленькие улучшения backpropagation.
   Пара бит про "переобучение".
   Каноническая трактовка регуляризации.
   Попытка неканонической трактовки регуляризации. Часть 1.
   Таинства подбора гиперпараметров обучения нейросетей.
   Магия аппроксимации любой функции нейросетью.
  
  Иллюстрации:
   Тральщик "Наставник" от "Модели Kandinsky 2.1".
   Тральщики "Наставник" от "Модели Kandinsky 3.0".
   "Обучение трансформеров" от Copilot.
   Тральщик "Наставник" от "Модели Kandinsky 3.1".
   Визуализация обучения нейронок от kraidiky.
  
  ========
  
  29.03.2024 21:41
  
  Вместо предисловия.
  В оглавление.
  
  
  Какое бы ни было бы отношение к нынешнему ажиотажу вокруг больших и малых нейросетей,
  одно можно сказать совершенно определенно:
  методы "машинного обучения" доказали свою работоспособность
  в решении большого класса самых разнообразных задач.
  Естественно, до какого-то "идеала" или "универсальной отмычки"
  им еще достаточно далеко, но и то что достижимо сейчас по-настоящему поражает.
  Поэтому имеет смысл разобраться за счет чего это получается,
  и какие перспективы есть в плане повышения их эффективности.
  
  Для меня это еще и давно откладываемая "до лучших времен" тема
  по собственному обучению и проработки вопросов в области понимания ИИ-систем.
  Так уж совпало, что практически одновременно сразу "несколько звезд сошлись"
  на этом "ИИ-небосклоне".
  Сначала это была гипотеза о "концептуальном пространстве эмбеддингов", см:
  "Заковыристая" структура вектора эмбеддинга."
  "Каково же оно "пространство эмбеддингов" нейронки?"
  Разработка которой привела к формированию отдельного модуля:
  "Металоция неведомого. Модуль Э. Эмбеддинги."
  При написании которого, оформилась, в "первом чтении",
  концепция битовых нейронных сетей (БНС), см.:
  "Первая попытка сформулировать концепцию битовой нейронной сети (БНС)".
  
  И что интересно, в какой-то момент дальнейшее развитие этих направлений,
  так или иначе начинало явно тормозиться отсутствием какой-то ясности
  в понимании того как происходит процесс обучения нейросетей
  "не в принципе, а в кожухе".
  А еще зудящей "занозой" висел один непонятный вопрос из "обучения с подкреплением"
  о какой-то чудовищной неэффективности использования в этой технологии
  метода обучения "обратным распространением ошибки",
  этого поистине "золотого молотка" всего "машинного обучения" глубоких нейросетей.
  
  Т.е. общее представление как работает этот метод у меня естественно есть,
  но вот как увязать "особенности" этого метода, со всеми теми "непонятками",
  которые постоянно возникали, при попытках какого-то более глубокого понимания
  работы различных архитектур нейросетей как-то не возникало.
  Так что, желание разобраться в этой теме возникло достаточно давно,
  но как-то все не получалось найти какую-то "точку опоры" для такой работы.
  И вот наконец-то я получил от Copilot ссылку на достаточно "древний",
  но весьма актуальный и по сей день очень фундаментальный материал на эту тему.
  
  И я решил проработать его максимально основательно.
  Так что этот модуль, во всяком случае, в начале представляет
  мой личный конспект проработки темы "метода обратного распространения ошибки"
  с "пометками на полях", отражающими мое сегодняшнее понимание этой темы.
  В дальнейшем хочется и другие методы "машинного обучения" нейросетей
  проработать также основательно. Но это пока очень отдаленные планы.
  А пока это попытка разбора и анализа "обратного распространения"
  максимально внимательно и подробно.
  
  Не все там так просто, как может по первоначалу показаться.
  И дело даже не в формулах, а в самих концепциях и деталях их реализации.
  
  "Обучение трансформеров методом обратного распространения ошибки"
  Создано с помощью искусственного интеллекта Copilot на платформе DALL? E 3.
  
Обучение трансформеров методом обратного распространения ошибки:  OIG2.JBdgsVJoQ0.HZYsyaAzq.jpg Обучение трансформеров методом обратного распространения ошибки:  OIG2.hwEAzmEYp19BJaE_1gNb.jpg
Обучение трансформеров методом обратного распространения ошибки:  OIG2.o1fEuq2DaJ2x_thbF32Q.jpg Обучение трансформеров методом обратного распространения ошибки:  OIG2.p8Z4GoqP7E1I6TzHnNQb.jpg
  
  
  ========
  
  23.03.2024 16:31
  
  Начинаем с "азов".
  В оглавление.
  
  Итак начинаем глубокое погружение в метод "обратного распространения ошибки",
  - backpropagation -
  "священный грааль" сегодняшнего машинного обучения нейросетей.
  Сначала, как и полагается, "тренировочный заплыв" с "азов"
  по более простому, но от этого не менее интересному материалу:
  
  "Простейшая нейросеть: еще раз и подробнее".
  Автор: nulovkin
  https://habr.com/ru/articles/714988/.
  6 фев 2023 в 10:00
  
  //// Начало цитирования.
  
  Введение
  
  Некоторое время назад, во время учебы в институте,
  я решил понять принцип работы нейросетей.
  Усвоить его на уровне, необходимом,
  чтобы написать небольшую нейросеть самостоятельно.
  Начать я решил с книги Тарика Рашида "Создай свою нейросеть".
  Эта статья представляет из себя краткий конспект этой книги для тех,
  кто, как и я, столкнулся с трудностями во время изучения этой темы
  и этого учебника (не в последнюю очередь благодаря проблемам редактуры).
  В процессе я надеюсь разложить все по полочкам еще раз.
  Предположу, что перемножение матриц и взятие производной
  никого из читателей не смутят и сразу пойду дальше.
  
  Итак, машинное обучение это незаменимый инструмент для решения задач,
  которые легко решаются людьми, но не классическими программами.
  Ребенок легко поймет, что перед ним буква А, а не Д,
  однако программы без помощи машинного обучения справляются с этим
  весьма средне.
  И едва ли вообще справляются при минимальных помехах.
  Нейросети же уже сейчас решают многие задачи (включая эту)
  намного лучше людей.
  Их способность обучаться на примерах
  и выдавать верный результат поистине очаровывает,
  однако за ней лежит простая математика
  и сейчас я покажу это на примере перцептрона.
  
  Описание работы нейронов
  
  Рис. Перцептрон с тремя слоями по три нейрона
  
  Каждый нейрон (или узел) принимает сигналы от узлов предыдущего слоя
  и передает на следующий.
  Каждая связь между нейронами имеет собственный вес.
  
  Таким образом, входной сигнал узла 1 слоя 1 передается
  на узел 1 слоя 2 с коэффициентом w_{11},
  на узел 2 слоя 2 с коэффициентом w_{12}и так далее.
  Все сигналы, полученные узлом уровня 2 складываются.
  //// Вот это "складывается" и есть основная/ключевая идея перцептрона
  //// и, возможно, большинства нейросетевых архитектур.
  //// Во всяком случае этот "inductive bias" лежит в основе
  //// не только многих архитектурных решений в нейросетях,
  //// но и ключевого меnода обучения - "обратного распространения ошибок".
  //// Надеюсь, это удастся показать максимально наглядно,
  //// если и не в этом материале, то в последующих.
  Это его входной сигнал.
  Таким образом сигналы передаются с уровня на уровень, до выхода.
  
  Для того, чтобы результат был более предсказуемым,
  используется функция сглаживания,
  одна из самых популярных - сигмоида y=1/(1+e^-x).
  
  Рис. Сигмоида y=1/(1+e^-x)
  
  Если каждый узел обрабатывает поступивший сигнал функцией сглаживания,
  то можно быть полностью уверенным,
  что он не выйдет за пределы от 0 до 1.
  
  Таким образом, нейрон суммирует входные сигналы,
  умноженные на веса связей,
  берет сигмоиду от результата и подает на выход.
  
  Рис. Схема узла
  
  Еще раз, для всего слоя:
  
  А как это решает задачу?
  
  Итак, как же применить нейросеть для распознавания букв на картинке?
  
  Входным сигналом для этой картинки 28 на 28 будет последовательность
  из 784 чисел от 0 до 255,
  каждое из которых шифрует цвет соответствующего пикселя.
  Итак, на входном уровне должно быть 784 узла.
  
  Информация, которую нам необходимо получить на выходе
  это "какая цифра скорее всего на картинке".
  Всего 10 вариантов.
  Значит, на выходном уровне будет 10 узлов.
  Узел, на котором сигнал будет больше
  и будет ответом нейросети на задачу
  - например, для этой картинки в идеале все узлы выходного уровня
  должны показывать на выходе ноль, а пятый - единицу.
  
  Добавим еще уровень, чтобы переход не был таким резким.
  Допустим, нейросеть будет из трех слоев - 784, 100 и 10 узлов.
  Общепринятого метода выбора точного количества узлов на промежуточных слоях
  и количества самих промежуточных слоев не существуют
  //// Сколько лет прошло с момента разработки этой и подобной архитектур,
  //// просмотрены наверно тысячи вариантов, а более-менее точных закономерностей
  //// пока не обнаружено. Есть какие-то общие рекомендации,
  //// и не похоже что ситуация в ближайшее время как-то изменится.
  //// Похоже, большинство с этим смирилось.
  //// Это как раз тот случай, когда математика "бессильна",
  //// и рулит чистая "эвристика".
  - разве что проводить эксперименты, и сравнивать результаты.
  В нашем случае первый уровень представляет пиксели входного изображения,
  третий - распознанные цифры
  а второй каким-то трудноотслеживаемым образом
  представляет закономерности, подмножества пикселей,
  которые свойственны разным цифрам.
  
  Добавим матрицы
  
  Переведем правила распространения сигнала на язык математики.
  Задача получить сигналы нового слоя,
  то есть "Умножить вес каждого узла слоя 1 на его выходную связь,
  ведущую к узлу слоя 2
  и сложить"
  на удивление сильно подходит на описание умножения матриц.
  В самом деле, расположим в каждом столбце матрицы весов веса связей,
  исходящих из одного узла
  и умножим справа на столбец входных сигналов
  и получим выходной сигнал этого слоя в столбце получившейся матрицы.
  
  Рис. Получение сигнала следующего уровня по формуле X = W*I
  
  В строках же матрицы весов будут веса связей,
  ведущих в один узел нового слоя,
  каждый из которых умножается на вес порождающего его узла.
  Очень изящно!
  Разумеется, из-за правил перемножения матриц
  высота конечного столбца будет равна высоте матрицы весов,
  а высота матрицы входных сигналов -- ширине матрицы весов.
  Для перехода из первого слоя (784 узла) во второй (100 узлов)
  в матрице весов нашей задачи понадобится таблица в 100 строк и 784 столбца.
  
  Итак, вся загадка заключается в том,
  какими именно значениями заполнена матрица весов.
  Ее заполнение называется тренировкой нейросети.
  Затем останется лишь опросить нейросеть,
  то есть решить конкретную задачу:
  
  Подать на вход картинку, то есть столбец из 784 сигналов.
  
  Умножить на него справа таблицу весов 12.
  
  Применить сигмоиду для сглаживания результатов.
  
  На результат справа умножить таблицу весов 23.
  
  Применить сигмоиду.
  
  Взять номер узла с наибольшим значением.
  
  Таким образом, из 784 значений с помощью
  всего лишь двух матричных умножений и сглаживаний
  получилось 10 чисел в диапазоне от 0 до 1.
  Номер узла с самым большим из них
  это значение цифры на картинке, как ее распознала нейросеть.
  Насколько это соответствует истине,
  зависит от тренировки нейросети.
  
  Тренировка. Обратное распространение ошибок
  
  Метод обратного распространения ошибок это сердце нейросети.
  //// Хорошая метафора. Только относящаяся не к самой нейросети,
  //// а к ее способности выполнять что-то вразумительное.
  Его суть заключается в том,
  чтобы после получения значения ошибки на последнем слое
  передать правки на предыдущие слои.
  
  Один из основных существующих подходов
  - распределять ошибку пропорционально весам связей.
  //// В основе этого подхода, если вдуматься,
  //// лежит именно тот самый "принцип суммирования входов".
  
  Рис. Обратное распространение ошибок
  
  Или то же самое, но для нескольких узлов на внешнем слое:
  //// Т.е. попытка комплексно учесть различные значения ошибок
  //// на всех узлах выходного слоя.
  
  Рис. Обратное распространение ошибки с нескольких слоев
  
  Ошибка, то есть e,
  это разница между t - желаемым значением и o - значением на выходном слое:
  e = t - o.
  
  Как мы видим, o1 высчитывается из узлов первого слоя
  с помощью связей w11 и w12,
  а значит, именно их и нужно корректировать с помощью ошибки этого узла.
  Новое значение w11 зависит от доли w11 в сумме связей, ведущих к узлу:
  \frac{w11}{w11+w21}.
  Конечно, для w21 нужно заменить w11 в числителе на w21.
  
  Теперь можно было бы приступить
  и к, собственно обратному распространению ошибки.
  Использовать данные следующего слоя для работы с предыдущим:
  
  Однако, у нас нет целевых значений для скрытого слоя.
  Не беда.
  Просто сложим ошибки всех связей,
  исходящих из этого узла,
  и получим его ошибку!
  //// Это один из "ключевых моментов" в методе "обратного распространения ошибки".
  
  Сложим, получим значение ошибки и просто повторим все еще раз.
  Пример показан ниже:
  
  Еще раз, словами:
  Ошибку Oi умножаем на долю связи w_{ki}/\sum_i W_i
  в сумме связи отдельного узла k со всеми узлами следующего уровня,
  чтобы получить ошибку узла k предыдущего слоя.
  Затем все ошибки связей из узла k складываем
  и получаем его собственную ошибку.
  И так далее.
  
  Перепишем все вышесказанное в виде матриц:
  
  Рис. Получение ошибки предыдущего слоя
  
  Удобно. Но не до конца.
  Было бы здорово избавиться от всех этих отличающихся знаменателей.
  И деления. совсем не удобно делить тысячу узлов на тысячу разных сумм.
  Было бы здорово как-то все упростить.
  Тут нам поможет тот факт,
  что для написания нейросети позволено забыть математику за третий класс
  на практике у нас будет больше двух узлов в слое.
  И значение числителя (например 0,2, 0,4 или 0,9) гораздо важнее,
  чем значение знаменателя (например 9,7, 9,3 или 8,4).
  Что приводит нас к моему любимому моменту в книге.
  Нормирующий множитель?
  Да зачем он нам?
  //// Очень остроумное решение, так как все равно используемое значение
  //// коррекции/изменения веса нужно регулировать, чтобы "не промазать".
  //// так не проще ли сразу задавать какой-то определенный нормирующий коэффициент,
  //// заодно отказавшись от необходимости использовать фактического "нормирования".
  //// Выигрыш налицо. А вот "минус" от такого решения как-то неочевиден.
  //// А "бесплатный сыр", как известно, "только в мышеловке".
  //// Но как-то об этой особенности слышать еще не приходилось.
  
  Рис. Здорово упрощенный расчет ошибки скрытого слоя
  
  Мы получили донельзя простую формулу обратного распространения ошибки
  с помощью матрицы весов и ошибки наружного слоя.
  Обратите внимание, что столбцы матрицы весов
  здесь поменяны местами со строками и наоборот,
  то есть матрица транспонирована.
  
  Рис. Итоговая формула ОРО
  
  Однако, нужно держать в уме,
  что это просто иллюстрация для идеального случая
  и понимания концепции.
  В нашей нейросети все немного сложнее.
  
  Тренировка. Обновление весов
  
  Однако, нужно напомнить,
  что мы дважды применяем сигмоиду по мере расчета веса узла.
  Кроме того, представим, что на узле ошибка 0,3.
  Если мы изменим одну связь, ведущую к этому узлу,
  то изменение других связей может снова все испортить.
  А так быть не должно.
  Интуитивно кажется, что каждая связь должна меняться
  сообразно своей роли в ошибке.
  При этом эта роль это не просто доля веса,
  ведь мы дважды применяли сигмоиду!
  
  Итак, нам нужно свести ошибку каждого узла к нулю.
  //// Еще одна "аксиома", которая не подвергается сомнению.
  //// Хотя если ставится задача "стратегического" обучения, а не "тактического",
  //// то должна быть возможность и какого-то промежуточного "игнорирования ошибки".
  Ошибка зависит от множества переменных,
  каждая из которых влияет на результат по разному.
  Звучит как задача для производной!
  
  Здесь нам пригодится метод градиентного спуска.
  Если нам известно, что связь Wij влияет на общую ошибку,
  то просто посчитаем производную
  и сделаем шаг в направлении нуля.
  //// Вот так потихоньку происходит "скрещивание" "градиентного спуска"
  //// и "обратного распространения ошибки",
  //// образуя ту "гремучую смесь", которая и обеспечила нынешний прорыв
  //// в обучении глубоких нейросетей.
  
  Нам нужно минимизировать ошибку.
  Значит, наш шаг должен вести нас к нулевой O .
  Нам нужно узнать соответствующую Wij.
  И выполнить это для каждой связи в узле,
  а затем для каждого узла в слое.
  
  Рассмотрим это для нашей многомерной функции:
  нам нужно узнать такие значения W
  (такие координаты, только не на двумерной плоскости, а во множестве измерений.
  Но это не сильно все осложнить),
  чтобы значение O было минимальным
  (спуститься в самую глубокую яму на карте).
  
  Напомню, что нужно делать все более мелкие шаги,
  чтобы не пройти центр "ямы".
  Для этого нужен специальный коэффициент, убывающий во время обучения.
  
  Также вы можете подумать,
  что легко можно забрести в неправильную "яму",
  то есть ложный минимум:
  
  К счастью, по какой-то причине для задач, подобных нашей,
  большинство ложных минимумов располагаются
  близко от основного
  и почти также глубоки.
  //// Любопытный факт, так и не получивший какого-то внятного объяснения.
  //// А ведь за любой "непоняткой" обычно кроется какое-то интересное открытие
  //// и иногда фундаментальное. Вся история физики только об этом.
  Сейчас можно об этом не беспокоиться.
  
  Самое лучшее в методе градиентного спуска,
  это его устойчивость к ошибкам.
  Если попадется несколько ошибок в тренировочных данных,
  последующие примеры постепенно сгладят эффект.
  //// Нет, это не свойство "градиентного спуска"
  //// - это свойство обучения нейросетей
  //// методом "обратного распространения ошибок",
  //// следствие того самого свойства "близости ложных минимумов".
  //// Просто еще 45 лет назад исследовал этот "градиентный спуск",
  //// на достаточно сложном "рельефе задачи"
  //// и убедился, что свойством устойчивости "к ошибкам" сам по себе
  //// он в принципе не обладает.
  //// Что, кстати, и при обучении нейросетей тоже должно проявляться,
  //// и компенсируется именно "последующими примерами" и backpropagation.
  
  Как на самом деле посчитать ошибку
  
  //// Тут очень рекомендую переключится на оригинальный текст,
  //// так как без иллюстраций понять дальнейший цитируемый текст в деталях
  //// весьма затруднительно.
  
  Для начала, вспомним, что ошибиться можно в обе стороны.
  А значит, значения ошибок будут как отрицательные, так и положительные.
  Тогда сумма ошибок может оказаться не тем больше, чем больше ошибки,
  а просто близкой к нулю.
  Значит, e = t-o как значение ошибки использовать нельзя.
  Приходит на ум модуль: e = |t-o|,
   чтобы избежать отрицательных значений.
  Однако тогда функция будет вести себя странно в районе нуля.
  //// Т.е. более разумное задание ошибки, конфликтует с чем-то еще,
  //// так до конца и невыясненным.
  Лучший вариант из всех для оценки ошибки это e=(t-o)^2.
  
  Теперь, когда мы исправили проблему подсчета ошибок,
  попробуем посчитать производную.
  dE/dWij.
  Это выражение представляет изменение значения ошибки при изменении веса узла.
  
  Перепишем функцию оценки ошибки:
  
  Рис. Е это показатель суммы всех ошибок.
  
  Получившееся выражение можно сразу упростить.
  Ошибка не зависит от всех значений на узлах,
  только от тех, что соединены с узлом k.
  Упростим выражение:
  
  Рис. Упрощенное представление функции
  
  Воспользуемся цепным правилом дифференцирования сложных функций:
  
  Теперь мы можем работать с частями этого уравнения по отдельности:
  
  \frac{dE}{do_k} = \frac{d(t_k-o_k)^2}{{do_k}} = -2(t_k-o_k)
  
  В осталось разобраться со второй частью,
  а первую подставим в общее уравнение:
  
  Перепишем выходной сигнал o_k в явном виде:
  
  Мы заменили выходной сигнал на сумму произведений каждой из связей,
  ведущих к этому узлу,
  на вес узла-источника.
  
  Вот формула, по которой дифференцируется сигмоида
  (и как хорошо, что нам не нужно это доказывать.
  Это работает):
  
  Применим это к нашей формуле и получим:
  
  Обратите внимание на то, что в последней формуле появился сомножитель
  (Одна из смущающих вещей в редактуре учебника.
  В этой формуле j под суммой и j в остальных местах, разумеется, разные,
  хотя оба представляют предыдущий уровень,
  так что здесь я обозначу индекс под суммой i.
  j находится в промежутке от 0 до i.):
  
  o_j=\frac{d(\sum_iw_{ik}*o_i)}{dw_{jk}}.
  
  Это результат применения цепного правила дифференцирования сложной функции,
  то есть производная выражения в скобках сигмоиды.
  Возможно вы, как и я поначалу, не поняли,
  почему оно именно такое.
  Что ж, это значение представляет зависимость суммы всех связей с узлом k,
  умноженных на веса их узлов от веса одной связи.
  Поскольку производная суммы равна сумме производных,
  а все прочие веса кроме w_{jk} относительно него считаются константами,
  то:
  
  \frac{d(\sum_iw_{ik}*o_i)}{dw_{jk}} = \frac{d(w_{jk}*o_j)}{dw_{jk}} = o_j * w_{jk}^0 = o_j.
  
  Таким образом, окончательный вид функции для изменения узлов
  предпоследнего слоя это
  (еще раз, помните, что на картинках j под суммой это не тот же j, что и снаружи):
  //// Да, с индексами у автора "некоторая двузначность".
  
  Рис. Градиент функции ошибки связей с выходным слоем
  
  Чтобы таким же образом изменить узлы слоя до него,
  подставим нужные связи и заменим очевидную финальную ошибку
  на посчитанную ранее ошибку скрытого слоя e_k:
  
  Рис. Градиент функции ошибки связей со скрытым слоем
  
  Применим нашу формулу, отображая тот факт,
  что результат необходимо умножить на коэфициент обучения,
  а градиент по знаку противоположен изменению связей:
  
  Перенесем W налево и покажем,
  как выглядят эти вычисления в матричной записи
  (коэффициент обучения для наглядности опущен),
  Е это значение ошибки узла,
  S это сумма произведений весов, ведущих к одному узлу,
  на их связь с этим узлом,
  на которую применили сигмоиду,
  O это сигнал на выходе из предыдущего слоя:
  
  Перепишем формулу целиком в удобном матричном виде
  (как можно было заметить в предыдущей формуле,
  последний сомножитель это транспонированная матрица
  выходных сигналов предыдущего слоя):
  
  Эту формулу будет удобно использовать в коде.
  
  Разбор примера обновления коэффициентов
  
  Если вы не совсем поняли с первого раза,
  какие значения куда подставлять,
  то разберем такой пример:
  
  Возьмем конкретно первый узел выходного слоя, где e_1=0,8.
  Мы хотим обновить весовой коэффициент w_{11}
  для связи между последними двумя слоями.
  Вспомним формулу градиента ошибки:
  
  Вместо (t_k-o_k)подставим нашу ошибку e_1=0,8.
  
  Сумма \sum _jw_{jk}o_j в данном случае равна (2,0*0,4) + (3,0*0,5) = 2,3.
  //// Тут, похоже, автор сам запутался в своих обозначениях.
  //// Во всяком случае, откуда у него взялось произведение (3,0*0,5)
  //// я так и не смог найти, хотя честно полчаса сравнивал самые
  //// разные иллюстрации и формулы в этом тексте.
  //// Откуда берется 3,0 - понятно, а вот откуда 0,5 - нет.
  //// И почему они объединяются в одном произведении в данном конкретном месте
  //// - тем более. И в комментариях никто не обратил на это внимание.
  //// Так что, считать, что я полностью понял этот материал,
  //// наверно, преждевременно.
  
  Сигмоида 1/(1+e^{-2,3}) = 0,909.
  
  Сигнал o_1=0,4.
  
  Следовательно, все значение в целом составит
  -0,8*0,909*(1-0,909)*0,4 = -0,02647.
  
  Допустим, что коэффициент обучения составляет 0,1.
  Тогда изменение веса составит -0,1*(-0,02647)=+0,002647.
  Это и есть тот довесок,
  который нам нужно добавить в связь w_{11}.
  Новое значение ее веса составит 2,002647.
  
  Еще несколько тысяч таких изменений и чаша наша полна.
  
  Пара слов о подготовке данных
  
  Рис. График нашей сигмоиды
  
  Как мы видим, при больших значениях входного сигнала
  значение сигмоиды будет изменяться очень слабо,
  вне зависимости от знака.
  Это будет означать, что нейросеть почти не будет изменяться
  и веса останутся после обучения почти такими же.
  Значит, весовые коэффициенты должны располагаться поближе к нулю
  (но не слишком, это может вызвать проблемы
  с подсчетами из-за ограничений формата с плавающей точкой).
  Нам хорошо подойдет масштабирование входных сигналов от 0,0 до 1,0
  - еще и потому, что именно такие сигналы обеспечивает сигмоида.
  Значение никогда не выйдет за пределы (0;1).
  
  Для работы нейросети необходимо указать
  начальные значения весовых коэффициентов.
  Причем, это не могут быть нули или просто одинаковые значения
  - в таких условиях они получат одинаковые правки
  и останутся совпадающими после обучения,
  что явно не даст нам хороших результатов.
  Остается указать случайные значения в приемлемом диапазоне,
  например от -1,0 до +1,0.
  Однако очевидно, что если значения весов в начале обучения
  будут близки к максимальным, то нейросеть может быстро насытиться.
  Это рассуждение, подкрепленное наблюдениями, породило эмпирическое правило:
  весовые коэффициенты должны выбираться из диапазона,
  приблизительно оцененного обратной величиной корня из количества связей,
  ведущих к узлу.
  Если к узлу ведут 3 связи, его вес должен быть случайным значением
  в промежутке (0;\frac{1}{\sqrt3}).
  
  Итоги
  
  Эта статья, представляющая конспект-пересказ
  книги Тарика Рашида "Создай свою нейросеть"
  призвана объяснить некоторые детали того,
  как проектируется и работает простой перцептрон
  и обратное распространение ошибок в нем.
  Я написал ее для того, чтобы охватить всю картину вместе,
  однако даже после столь внимательного погружения в материал
  и прояснения каждой его части
  я не уверен, что смогу написать что-то похожее,
  например распознавалку знаков,
  без заглядывания в книгу.
  Однако, я намного ближе к этому, чем какое-то время назад.
  
  Я надеюсь, что эта статья поможет таким же как я новичкам в мире нейросетей,
  кто не понял все аспекты процесса с первого раза и забросил книгу на пару лет.
  
  Я приветствую критику как от них, так и от всех остальных,
  касательно фактических ошибок, стиля подачи материала,
  неточностей, упущений и других проблем статьи,
  которые, я уверен, найдутся, поскольку раньше я ничего подобного не писал.
  
  Спасибо вам всем!
  
  ........
  
  //// Из комментариев.
  
  Akon32
  7 фев 2023 в 20:33
  
  Я помню, что градиент считается как dE/dw,
  а дальше идёт матричное дифференцирование.
  Но это теория.
  На практике градиент автоматически считает библиотека типа Keras,
  причём она считает производные для всех слоёв.
  Практика здесь далеко ушла от базовой теории
  - руками градиенты считать не нужно,
  поэтому базовая теория сейчас малополезна.
  
  Поиск по тегу "перцептрон" даёт штук 8 таких начальных статей,
  но мне кажется, что их больше (можно посмотреть по другим ключевым словам).
  Я такие комментарии по поводу перцептронов уже несколько раз писал,
  эта тема очень проста, вот и всплывает периодически.
  И это только на Хабре.
  
  ........
  
  //// Конец цитирования.
  
  Как видно из комментариев, разбираться в том как работает backpropagation
  в деталях и в "кожухе" уже давно "немодно".
  Есть "теория, доказавшая свою истину на практике",
  есть заслуживающие доверия библиотеки,
  так кому может быть интересна эта тема, покрытая "пылью веков"?
  
  А мне вот было по-настоящему интересно разобраться в том,
  что уже считается "незыблемыми аксиомами",
  а что осталось "за бортом внимания",
  именно потому, что как-то не укладывается в эти "аксиомы".
  В общем, если не считать некоторых мелких недочетов,
  то я считаю этот материал очень и очень достойным не только прочтения,
  но и внимательного изучения.
  
  Итак, "пробное погружение" в backpropagation можно считать завершенным,
  и теперь можно перейти к самой "мякотке" или, наоборот, "фундаменталке".
  
  
  =======
  
  18.03.2024 20:16
  
  "Повторение мать учения".
  В оглавление.
  
  Собственно получив ссылку на этот материал от Copilot,
  и бегло просмотрев оглавление, и появилась задумка сделать этот модуль,
  оттолкнувшись от детального разбора серьезного учебника
  по глубокому обучению нейросетей.
  Торопиться просто пробежаться по этой теме нет никакого смысла,
  так как задача стоит в том, чтобы попытаться нащупать
  не просто "болевые точки" метода обратного распространения ошибки,
  а ключевые концепции,
  которые можно было бы как-то иначе переформулировать/представить/использовать.
  И, может быть, придумать что-то более подходящее/удобное
  для битовых нейронных сетей (БНС).
  
  Так что предлагаю, поудобнее устроиться в кресле,
  и погрузиться в первую часть "архивных раскопок":
  
  "Нейросети и глубокое обучение,
  глава 1: использование нейросетей для распознавания рукописных цифр".
  Автор: SLY_G (Вячеслав Голованов)
  https://habr.com/ru/articles/456738/.
  20 июн 2019 в 10:00
  
  Автор оригинала: Michael Nielsen
  http://neuralnetworksanddeeplearning.com/chap1.html
  
  //// Начало цитирования.
  
  Перед вами - перевод свободной онлайн-книги Майкла Нильсена
  ,
  http://creativecommons.org/licenses/by-nc/3.0/deed.en_GB
  распространяемой под лицензией
  Creative Commons Attribution-NonCommercial 3.0 Unported License.
  
  ......
  
  Содержание
  
  Глава 1: использование нейросетей для распознавания рукописных цифр
  https://habr.com/ru/post/456738/
  
  Глава 2: как работает алгоритм обратного распространения
  https://habr.com/ru/post/457980/
  
  Глава 3:
  
  ч.1: улучшение способа обучения нейросетей
  https://habr.com/ru/post/458724/
  
  ч.2: почему регуляризация помогает уменьшать переобучение?
  https://habr.com/ru/post/459816/
  
  ч.3: как выбрать гиперпараметры нейросети?
  https://habr.com/ru/post/460711/
  
  Глава 4: визуальное доказательство того,
  что нейросети способны вычислить любую функцию
  https://habr.com/ru/post/461659/
  
  Глава 5: почему глубокие нейросети так сложно обучать?
  https://habr.com/ru/post/462381/
  
  Глава 6:
  
  ч.1: глубокое обучение
  https://habr.com/ru/post/463171/
  
  ч.2: недавний прогресс в распознавании изображений
  https://habr.com/ru/post/464039/
  
  Послесловие: существует ли простой алгоритм для создания интеллекта?
  https://habr.com/ru/post/464735/
  ......
  
  Нейросети - прекрасная программная парадигма,
  созданная под влиянием биологии,
  и позволяющая компьютеру учиться на основе наблюдений.
  
  Глубокое обучение - мощный набор техник обучения нейросетей.
  
  Нейросети (НС) и глубокое обучение (ГО)
  на сегодня дают наилучшее решение многих задач
  из областей распознавания изображений, голоса и обработки естественного языка.
  Этот учебник научит вас многим ключевым концепциям, лежащим в основе НС и ГО.
  
  ......
  
  Автоматическое обучение на основе данных звучит многообещающе.
  Однако до 2006 года мы не знали, как обучать НС так,
  чтобы они могли превзойти более традиционные подходы,
  за исключением нескольких особых случаев.
  В 2006 были открыты техники обучения т.н. глубоких нейросетей (ГНС).
  Теперь эти техники известны, как глубокое обучение (ГО).
  Их продолжали разрабатывать,
  и сегодня ГНС и ГО достигли потрясающих результатов во многих важных задачах,
  связанных с компьютерным зрением, распознаванием речи
  и обработки естественного языка.
  
  ......
  
  Подход с упором на принципы
  
  Одно из убеждений, лежащих в основе книги, состоит в том,
  что лучше овладеть твёрдым пониманием ключевых принципов НС и ГО,
  чем нахватать знаний из длинного списка различных идей.
  Если вы хорошо разберётесь в ключевых идеях,
  вы быстро поймёте и другой новый материал.
  
  ......
  
  Практический подход
  
  ......
  
  Упражнения и задачи
  
  .......
  
  Глава 1
  
  ......
  
  В этой главе мы напишем компьютерную программу,
  реализующую НС, обучающуюся распознавать рукописные цифры.
  Программа будет всего в 74 строки длиной,
  и не будет использовать специальных библиотек для НС.
  Однако эта короткая программа сможет распознавать рукописные цифры
  с точностью более 96%, не требуя вмешательства человека.
  Более того, в дальнейших главах мы разработаем идеи,
  способные улучшить точность до 99% и более.
  
  ......
  
  Перцептроны
  
  ......
  
  Так как же работают перцептроны?
  Перцептрон принимает на вход несколько двоичных чисел x1,x2,...
  и выдаёт одно двоичное число:
  
  .....
  
  Розенблатт предложил простое правило для вычисления результата.
  Он ввёл веса, w1, w2, вещественные числа,
  выражающие важность соответствующих входных чисел для результатов.
  Выход нейрона, 0 или 1, определяется тем,
  меньше или больше некоего порога [threshold]
  взвешенная сумма $\sum_j w_jx_j$.
  Как и веса, порог - вещественное число, параметр нейрона.
  
  ......
  
  Кстати, когда я определял перцептрон,
  я сказал, что у него есть только одно выходное значение.
  Но в сети наверху перцептроны выглядят так,
  будто у них есть несколько выходных значений.
  На самом деле, выход у них только один.
  Множество выходных стрелок - просто удобный способ показать,
  что выход перцептрона используется
  как вход нескольких других перцептронов.
  Это менее громоздко, чем рисовать один разветвляющийся выход.
  
  ......
  
  Я описал перцептроны с точки зрения метода взвешивания свидетельств
  с целью принятия решения.
  Ещё один метод их использования - вычисление элементарных логических функций,
  которые мы обычно считаем основными вычислениями,
  таких, как AND, OR и NAND.
  
  ........
  
  Этот пример показывает,
  что можно использовать перцептроны для вычисления базовых логических функций.
  На самом деле, мы можем использовать сети перцептронов
  для вычисления вообще любых логических функций.
  Дело в том, что логический вентиль NAND универсален для вычислений
  - на его основе можно строить любые вычисления.
  //// Теоретически можем, а вот практически это оказывается не так.
  //// Например, имеются трудности с периодическими функциями активации,
  //// со структурами, которые можно принудительно заставить работать как хочется,
  //// но вот обучить их аналогичному поведению очень часто не удается.
  //// И это одна из проблем, которые заставляют снова и снова "изучать под лупой"
  //// все аспекты этой технологии, состоящей из двух половинок:
  //// концепции перцептрона и метода обучения обратным распространением ошибки.
  
  .......
  
  Пример с сумматором демонстрирует,
  как можно использовать сеть из перцептронов для симуляции контура,
  содержащего множество вентилей NAND.
  А поскольку эти вентили универсальны для вычислений,
  следовательно, и перцептроны универсальны для вычислений.
  //// Вот как раз именно этот пример можно рассматривать
  //// и как неуниверсальности существующего метода backpropagation,
  //// так как обучить нечто подобное с высокой точность практически невероятно.
  
  Вычислительная универсальность перцептронов
  одновременно обнадёживает и разочаровывает.
  Обнадёживает она, гарантируя, что сеть из перцептронов
  может быть настолько же мощной,
  насколько любое другое вычислительное устройство.
  Разочаровывает, создавая впечатление,
  что перцептроны - всего лишь новый тип логического вентиля NAND.
  Так себе открытие!
  
  Однако на самом деле ситуация лучше.
  Оказывается, что мы можем разработать обучающие алгоритмы,
  способные автоматически подстраивать веса
  и смещения сети из искусственных нейронов.
  Эта подстройка происходит в ответ на внешние стимулы,
  без прямого вмешательства программиста.
  Эти обучающие алгоритмы позволяют нам использовать
  искусственные нейроны способом,
  радикально отличным от обычных логических вентилей.
  Вместо того, чтобы явно прописывать контур из вентилей NAND и других,
  наши нейросети могут просто обучиться решать задачи,
  иногда такие, для которых было бы чрезвычайно сложно напрямую
  спроектировать обычный контур.
  //// А иногда неспособных воспроизвести достаточно тривиальную зависимость,
  //// во всяком случае, при сопоставимом количестве узлов и связей.
  //// Обычно в такого рода "восхвалениях нейросетей" и "машинного обучения"
  //// "почему-то" забывают упомянуть о том, что решаемые таким образом задачи
  //// требуют определенного минимального "масштаба" нейросети,
  //// чтобы она как-то более-менее могла соответствовать поставленной задаче.
  //// А вот какой должен быть этот "масштаб" "теория машинного обучения"
  //// опять "почему-то" "скромно умалчивает".
  //// И тут, по-моему, проблема в том, что полного понимания ни возможностей,
  //// ни ограничений backpropagation пока нет.
  //// А есть как-то работающие формулы и заточенные под них библиотеки,
  //// а когда их использование явно не достаточно
  //// обнаруживается только "постфактум", то бишь, экспериментально.
  
  
  Сигмоидные нейроны
  
  Обучающие алгоритмы - это прекрасно.
  Однако как разработать такой алгоритм для нейросети?
  Допустим, у нас есть сеть перцептронов,
  которую мы хотим использовать для обучения решения задачи.
  Допустим, входными данными сети могут быть
  пиксели отсканированного изображения рукописной цифры.
  И мы хотим, чтобы сеть узнала веса и смещения,
  необходимые для правильной классификации цифры.
  Чтобы понять, как может работать такое обучение,
  представим, что мы немного меняем некий вес (или смещение) в сети.
  Мы хотим,
  чтобы это небольшое изменение привело
  к небольшому изменению выходных данных сети.
  Как мы скоро увидим,
  это свойство делает возможным обучение.
  //// Очень важное утверждение для понимания всей существующей концепции
  //// не только перцептрона, но и всей методики обучения нейросетей.
  //// Т.е. для существующей концепции обучения СУЩЕСТВЕННО ВАЖНО
  //// ЛИНЕЙНОСТЬ и НЕПРЕРЫВНОСТЬ представлений данных в отдельном перцептроне,
  //// и даже во всей нейросети.
  
  //// Хочется отметить, что именно это требование и делает невозможным
  //// "реализацию любых функций" нейросетью минимального размера в общем случае.
  //// Т.е. теоретически "нейросеть минимального размера" способна реализовать
  //// достаточно сложные функциональные зависимости,
  //// если она будет хоть частично НЕЛИНЕЙНА или ПРЕРЫВИСТА.
  //// Но это входит в противоречие с требованиями "обучаемости"
  //// с помощью "линейных и непрерывных" представлений данных.
  //// И отсюда растут корни многих проблем обучения нейросетей,
  //// которые ЧАСТИЧНО удается решать за счет увеличения "масштаба" нейросетей.
  //// И решение это основано именно на "суммировании",
  //// т.е. осреднении данных, которые, сами по себе, могут быть
  //// и нелинейны и прерывисты, с точки зрения их интерпретации.
  
  Схематично мы хотим следующего
  (очевидно, такая сеть слишком проста, чтобы распознавать рукописный ввод!):
  
  Если бы небольшое изменение веса (или смещения) приводило бы
  к небольшому изменению результата на выходе,
  мы могли бы изменять веса и смещения,
  чтобы наша сеть вела себя чуть ближе к желаемому.
  К примеру, допустим, что сеть неправильно отнесла изображение к <8>,
  хотя должна была к <9>.
  Мы могли бы разобраться, как внести небольшое изменение в веса и смещения,
  чтобы сеть немного ближе подобралась к классификации изображения, как <9>.
  А потом мы бы повторяли это, изменяя веса и смещения снова и снова,
  чтобы получать всё лучший и лучший результат.
  Сеть бы училась.
  //// Данная концепция предполагает "гладкость" и "непрерывность" чего-то там,
  //// о чем также обычно "скромно умалчивается".
  //// Все как в старой солдатской песне:
  //// "Гладко было на бумаге,
  //// да забыли про овраги,
  //// а по ним ходить..."
  
  Проблема в том, что если в сети есть перцептроны,
  такого не происходит.
  Небольшое изменение весов или смещения любого перцептрона
  иногда может привести к изменению его выхода на противоположный,
  допустим, с 0 на 1.
  Такой переворот может изменить поведение остальной сети
  очень сложным образом.
  И даже если теперь наша <9> и будет правильно распознана,
  поведение сети со всеми остальными изображениями,
  вероятно, полностью изменилось таким образом,
  который сложно контролировать.
  Из-за этого сложно представить,
  как можно постепенно подстраивать веса и смещения,
  чтобы сеть постепенно приближалась к желаемому поведению.
  Возможно, существует некий хитроумный способ обойти эту проблему.
  Но нет никакого простого решения задачи обучения сети из перцептронов.
  //// Вот оно, честное признание - неизвестно как обучать "прерывистую"
  //// или дискретную систему, поэтому изменяем систему на "квазинепрерывную".
  //// Ну что ж, вполне рабочее решение:
  //// "За неимением гербовой, пишем на простой."
  
  //// Но это не значит, что для дискретных систем принципиально
  //// нет возможности найти обучающий алгоритм.
  
  //// Но зато это значит, что если где-то снова в системе,
  //// не предназначенной для работы с нелинейностью и дискретностью/прерывистостью,
  //// такое поведение все-таки как-то возникнет,
  //// то скорее всего для такой системы это плохо кончится.
  //// И, очень вероятно, что такое периодически происходит,
  //// и единственный хоть какой-то выход из этого -
  //// дальнейшее увеличение "масштаба" системы в надежде,
  //// что суммирование/усреднение "сгладит" эту нелинейность/прерывистость.
  //// Ну, или кардинальная перестройка структуры нейросети,
  //// причем, опять же практически вслепую.
  
  Эту проблему можно обойти,
  введя новый тип искусственного нейрона под названием сигмоидный нейрон.
  Они похожи на перцептроны,
  но изменены так, чтобы небольшие изменения весов и смещений
  приводили только к небольшим изменениям выходных данных.
  Это основной факт,
  который позволит сети из сигмоидных нейронов обучаться.
  //// А вот с этим согласен полностью.
  //// Только добавлю, что в качестве функции активации могут быть и другие функции,
  //// но только монотонные и непрерывные,
  //// если использовать backpropagation в существующем виде.
  
  Давайте я опишу сигмоидный нейрон.
  Рисовать мы их будем так же, как перцептроны:
  
  У него точно так же есть входные данные x1, x2,..
  Но вместо того, чтобы приравниваться к 0 или 1,
  эти входы могут иметь любое значение в промежутке от 0 до 1.
  К примеру, величина 0,638 будет допустимым значением входных данных
  для сигмоидного нейрона (СН).
  Так же, как у перцептрона, у СН есть веса для каждого входа, w1, w2,...
  и общее смещение b.
  Но его выходным значением будет не 0 или 1.
  Это будет ?(w?x+b), где ? - это сигмоида.
  
  Кстати, ? иногда называют логистической функцией,
  а этот класс нейронов - логистическими нейронами.
  Эту терминологию полезно запомнить,
  поскольку эти термины используют многие люди, работающие с нейросетями.
  Однако мы будем придерживаться сигмоидной терминологии.
  
  Определяется функция так:
  
  $ \sigma(z) \equiv \frac{1}{1+e^{-z}} \tag{3} $
  
  В нашем случае выходное значение сигмоидного нейрона
  с входными данными x1, x2,... весами w1, w2,... и смещением b
  будет считаться, как:
  
  $ \frac{1}{1+exp(-\sum_j w_jx_j - b)} \tag{4} $
  
  На первый взгляд, СН кажутся совсем не похожими на нейроны.
  Алгебраический вид сигмоиды может показаться запутанным и малопонятным,
  если вы с ним не знакомы.
  На самом деле между перцептронами и СН есть много общего,
  и алгебраическая форма сигмоиды оказывается больше технической подробностью,
  нежели серьёзным барьером к пониманию.
  
  ......
  
  Что насчёт алгебраического вида ??
  Как нам его понять?
  На самом деле, точная форма ? не так уж важна
  - важна форма функции на графике.
  Вот она:
  
  Это сглаженный вариант ступенчатой функции:
  
  Если бы ? была ступенчатой,
  тогда СН был бы перцептроном,
  поскольку у него на выходе наблюдались бы 0 или 1
  в зависимости от знака w ? x + b
  (ну, на самом деле при z = 0 перцептрон выдаёт 0,
  а ступенчатая функция - 1,
  так что в одной этой точке функцию пришлось бы поменять).
  
  Используя реальную функцию ?, мы получаем сглаженный перцептрон.
  И главным тут является гладкость функции,
  а не её точная форма.
  Гладкость означает, что небольшие изменения ?wj весов и ?b
  смещений дадут небольшие изменения ?output выходных данных.
  //// Вот где-то в этом месте неявно "кувалдой вбивается в сознание"
  //// что если уж у нас используются только сигмоидные нейроны,
  //// то это гарантия того, что "гладкость" гарантируется
  //// для всего поведения нейросети, точнее, ее внутренних данных.
  //// Это такая "ложная аксиома", о которой практически никто и не упоминает.
  //// Во всяком случае, во многих обучающих курсах об этом нет ни слова.
  
  Алгебра говорит нам, что ?output хорошо аппроксимируется так:
  
  ......
  
  Где суммирование идёт по всем весам wj,
  а ?output/?wj и ?output/?b обозначают
  частные производные выходных данных по wj и b соответственно.
  Не паникуйте, если чувствуете себя неуверенно в компании частных производных!
  Хотя формула и выглядит сложной, со всеми этими частными производными,
  на самом деле она говорит нечто совсем простое (и полезное):
  ?output - это линейная функция, зависящая от ?wj и ?b весов и смещения.
  //// Еще раз напоминаю, что это "суммирование" ключевой принцип
  //// и самого перцептрона/нейрона и всей концепции нейросети.
  Её линейность облегчает выбор небольших изменений весов
  и смещений для достижения любого желаемого небольшого смещения выходных данных.
  Так что, хотя по качественному поведению СН похожи на перцептроны,
  они облегчают понимание того,
  как можно изменить выход, меняя веса и смещения.
  
  ........
  
  Если для нас имеет значение общая форма ?,
  а не её точный вид, то почему мы используем именно такую формулу (3)?
  На самом деле позднее мы иногда будем рассматривать нейроны,
  выход которых равняется f(w ? x + b),
  где f() - некая другая функция активации.
  Главное, что меняется при смене функции - значения частных производных
  в уравнении (5).
  Оказывается, что когда мы потом подсчитываем эти частные производные,
  использование ? сильно упрощает алгебру,
  поскольку у экспонент есть очень приятные свойства при дифференцировании.
  В любом, случае, ? часто используется в работе с нейросетями,
  и чаще всего в этой книге мы будем использовать такую функцию активации.
  
  Как интерпретировать результат работы СН? Очевидно,
  главным различием между перцептронами и СН будет то,
  что СН не выдают только 0 или 1.
  Их выходными данными может быть любое вещественное число от 0 до 1,
  так что значения типа 0,173 или 0,689 являются допустимыми.
  Это может быть полезно, к примеру,
  если вам нужно, чтобы выходное значение обозначало,
  к примеру, среднюю яркость пикселей изображения,
  поступившего на вход НС.
  Но иногда это может быть неудобно.
  Допустим, мы хотим, чтобы выход сети говорил о том,
  что <на вход поступило изображение 9>
  или <входящее изображение не 9>.
  Очевидно, проще было бы, если бы выходные значения были 0 или 1,
  как у перцептрона.
  Но на практике мы можем договориться, что любое выходное значение
  не меньше 0,5 обозначало бы <9> на входе,
  а любое значение меньше 0,5, обозначало бы, что это <не 9>.
  //// Надо отметить, что переход к таким непрерывным значениям нейронов,
  //// это не только ограничение их возможностей в одном качестве,
  //// но и, наоборот, расширение их возможностей в другом качестве,
  //// например, указание соответствующей "яркости пикселей",
  //// или представив это значение как "координату" или "вероятность",
  //// но об этом более подробно будет существенно ниже,
  //// так как там тоже есть свои "подводные камни" и их немало.
  
  //// "Нет худа, без добра" и наоборот.
  //// Нужно только правильно использовать разные возможности
  //// для решения конкретных задач,
  //// а не пытаться все задачи решать одним и тем же "молотком",
  //// что, к сожалению, наблюдается слишком часто.
  //// Но для этого требуется уже не "знание", а "понимание".
  
  ......
  
  Архитектура нейросетей
  
  ......
  
  Как я уже упоминал, самый левый слой в сети называется входным слоем,
  а его нейроны - входными нейронами.
  Самый правый, или выходной слой, содержит выходные нейроны,
  или, как в нашем случае, один выходной нейрон.
  Средний слой называется скрытым,
  поскольку его нейроны не являются ни входными, ни выходными.
  Термин <скрытый>, возможно, звучит немного загадочно
  - впервые услышав его, я решил, что у него должна быть
  некая глубокая философская или математическая важность
  - однако он означает лишь <не вход и не выход>.
  У сети выше есть только один скрытый слой,
  но у некоторых сетей есть по нескольку скрытых слоёв.
  
  .......
  
  Это может сбивать с толку,
  но по историческим причинам такие сети с несколькими слоями
  иногда называют многослойными перцептронами, MLP,
  несмотря на то, что они состоят из сигмоидных нейронов, а не перцептронов.
  
  ......
  
  И если проектирование входных и выходных слоёв часто является простой задачей,
  то проектирование скрытых слоёв может оказаться сложным искусством.
  В частности, невозможно описать процесс разработки скрытых слоёв
  при помощи нескольких простых практических правил.
  //// Т.е. даже составители учебников по нейросетям, а не только студенты,
  //// не знают каких-то действительно теоретически обоснованных рекомендаций.
  //// Говорят, что теория проверяется практикой,
  //// а что говорить, когда теория не дает практических рекомендаций?
  Исследователи НС разработали множество эвристических правил
  проектирования скрытых слоёв, помогающих получать нужное поведение нейросетей.
  К примеру, такую эвристику можно использовать, чтобы понять,
  как достичь компромисса между количеством скрытых слоёв
  и временем, доступным для обучения сети.
  
  ........
  
  Пока что мы обсуждали НС, в которых выходные данные одного слоя
  используются в качестве входных для следующего.
  Такие сети называются нейросетями прямого распространения.
  Это значит, что в сети нет петель - информация всегда проходит вперёд,
  и никогда не скармливается назад.
  Если бы у нас были петли, мы бы встречали ситуации,
  в которых входные данные сигмоиды зависели бы от выходных.
  Это было бы тяжело осмыслить, и таких петель мы не допускаем.
  //// Еще одно ограничение на "произвольный характер функций",
  //// потенциально реализуемых обучаемой нейросетью.
  
  Однако существуют и другие модели искусственных НС,
  в которых возможно использовать петли обратной связи.
  Эти модели называются рекуррентными нейронными сетями (РНС).
  Идея этих сетей в том, что их нейроны активируются
  на ограниченные промежутки времени.
  Эта активация может стимулировать другие нейтроны,
  которые могут активироваться чуть позже,
  также на ограниченное время.
  Это приводит к активации следующих нейронов,
  и со временем мы получаем каскад активированных нейронов.
  Петли в таких моделях не представляют проблем,
  поскольку выход нейрона влияет на его вход
  в некий более поздний момент,
  а не сразу.
  //// А как их обучают?
  
  РНС были не такими влиятельными, как НС прямого распространения,
  в частности потому, что обучающие алгоритмы для РНС
  пока что обладают меньшими возможностями.
  Однако РНС всё равно остаются чрезвычайно интересными.
  По духу работы они гораздо ближе к мозгу,
  чем НС прямого распространения.
  Возможно, что РНС смогут решить важные задачи,
  которые при помощи НС прямого распространения решаются с большими сложностями.
  Однако чтобы ограничить область нашего изучения,
  мы сконцентрируемся на более широко применяемых НС прямого распространения.
  
  Простая сеть классификации рукописных цифр
  
  .......
  
  В выходном слое сети 10 нейронов.
  Если активируется первый нейрон, то есть, его выходное значение ? 1,
  это говорит о том, что сеть считает, что на входе был 0.
  Если активируется второй нейрон, сеть считает, что на входе был 1.
  И так далее.
  Строго говоря, мы нумеруем выходные нейроны от 0 до 9,
  и смотрим, у какого из них значение активации было максимальным.
  Если это, допустим, нейрон ?6, тогда наша сеть считает,
  что на входе была цифра 6.
  И так далее.
  
  Вы можете задуматься над тем, зачем нам использовать десять нейронов.
  Ведь мы же хотим узнать, какой цифре от 0 до 9
  соответствует входное изображение.
  Естественно было бы использовать всего 4 выходных нейрона,
  каждый из которых принимал бы двоичное значение
  в зависимости от того, ближе его выходное значение к 0 или 1.
  Четырёх нейронов будет достаточно,
  поскольку 2*4=16,
  больше, чем 10 возможных значений.
  Зачем нашей сети использовать 10 нейронов?
  Это ведь неэффективно?
  Основание для этого эмпирическое;
  мы можем попробовать оба варианта сети,
  и окажется, что для данной задачи сеть с 10-ю выходными нейронами
  лучше обучается распознавать цифры,
  чем сеть с 4-мя.
  //// Как раз о таких случая и упоминалось в тезисе
  //// о сложности обучения нейросетей с нелинейными характеристиками.
  //// Причем именно о сложности, а не о невозможности такое сделать.
  //// Просто такая сеть будет либо работать много хуже,
  //// либо должна быть большего "масштаба", либо и то и другое вместе,
  //// и, в любом случае, дольше обучаема.
  Однако остаётся вопрос, почему же 10 выходных нейронов лучше.
  Есть ли какая-то эвристика,
  которая заранее сказала бы нам,
  что следует использовать 10 выходных нейронов вместо 4?
  
  Чтобы понять, почему, полезно подумать о том, что делает нейросеть.
  Рассмотрим сначала вариант с 10 выходными нейронами.
  Сконцентрируемся на первом выходном нейроне,
  который пытается решить,
  является ли входящее изображение нулём.
  Он делает это, взвешивая свидетельства, полученные из скрытого слоя.
  А что делают скрытые нейроны?
  Допустим, первый нейрон в скрытом слое определяет,
  есть ли на картинке что-то вроде такого:
  
  .......
  
  Делать это он может, назначая большие веса пикселям,
  совпадающим с этим изображением,
  и малые веса остальным.
  Точно так же допустим, что второй, третий и четвёртый нейроны
  в скрытом слое ищут, есть ли на изображении подобные фрагменты:
  
  .......
  
  Как вы могли догадаться, все вместе эти четыре фрагмента
  дают изображение 0,
  которое мы видели ранее:
  
  .......
  
  Итак, если четыре скрытых нейрона активируются,
  мы можем заключить, что цифра равна 0.
  Конечно, это не единственное свидетельство того, что там был изображён 0
  - мы можем получить 0 и множеством других способов
  (немного сдвигая указанные изображения или слегка искажая их).
  Однако можно точно сказать, что, по крайней мере,
  в этом случае мы можем заключить, что на входе был 0.
  
  Если предположить, что сеть работает так,
  можно дать правдоподобное объяснение тому,
  почему лучше использовать 10 выходных нейронов вместо 4.
  Если бы у нас было 4 выходных нейрона,
  тогда первый нейрон пытался бы решить,
  каков самый старший бит у входящей цифры.
  И нет простого способа связать самый старший бит с простыми формами,
  приведёнными выше.
  Сложно представить какие-то исторические причины,
  по которым части формы цифры были бы как-то связаны
  с самым старшим битом выходных данных.
  //// Хорошая иллюстрация. Очень рекомендую ее внимательно продумать.
  //// И почувствовать, как часто подобные ситуации возникают
  //// в работе и обучении более сложных нейросетей.
  
  Однако всё вышесказанное подкрепляется только эвристикой.
  Ничто не говорит в пользу того,
  что трёхслойная сеть должна работать так, как я сказал,
  а скрытые нейроны должны находить простые компоненты форм.
  Возможно, хитрый алгоритм обучения
  найдёт какие-нибудь значения весов,
  которые позволят нам использовать только 4 выходных нейрона.
  Однако в качестве эвристики мой способ работает неплохо,
  и может сэкономить вам значительное время
  при разработке хорошей архитектуры НС.
  //// Вот и еще одна иллюстрации того самого "вбитого кувалдой в мозг"
  //// представления входа нейрона как суммы непрерывных величин,
  //// и, соответствующего, желания оставить все как привычно/принято
  //// за счет какого-то "хитрого алгоритма обучения".
  //// Это не значит, что такого алгоритма нельзя придумать,
  //// просто в технике обычно такие задачи "в лоб" решаются очень редко,
  //// и куда чаще решение находится на "обходном пути".
  
  Упражнения
  
  Существует способ определить побитовое представление числа,
  добавив дополнительный слой к трёхслойной сети.
  Дополнительный слой преобразует выходные значения предыдущего слоя
  в двоичный формат, как показано на рисунке ниже.
  Найдите наборы весов и смещений для нового выходного слоя.
  Предполагаем, что первые 3 слоя нейронов таковы,
  что правильный выход с третьего слоя (бывший выходной слой)
  активируется значениями не менее 0,99,
  а неправильные выходные значения не превышают 0,01.
  //// А это как раз пример нейросети которая,
  //// может решать поставленную задачу,
  //// если нужные веса будут установлены принудительно.
  //// А вот то, что они могут в такой сети быть получены в результате обучения,
  //// лично у меня, вызывает большие сомнения. Слышать о подобном не приходилось.
  //// Но могу и ошибаться. Мало ли чудес бывает в этом мире.
  
  Обучение с градиентным спуском
  
  Итак, у нас есть схема НС - как ей обучиться распознавать цифры?
  Первое, что нам понадобится - это обучающие данные,
  т.н. набор обучающих данных.
  
  .......
  
  Нам хочется найти алгоритм, позволяющим нам искать
  такие веса и смещения,
  чтобы выход сети приближался к y(x) для всех обучающих входных х.
  Чтобы количественно оценить приближение к этой цели,
  определим функцию стоимости
  (иногда называемую функцией потерь;
  в книге мы будем использовать функцию стоимости,
  однако имейте в виду и другое название):
  
  $ C(w, b) = \frac{1}{2n} \sum_x || y(x) - a ||^2 \tag{6} $
  
  Здесь
  w обозначает набор весов сети,
  b - набор смещений,
  n - количество обучающих входных данных,
  a - вектор выходных данных, когда х - входные данные,
  а сумма проходит по всем обучающим входным данным х.
  Выход, конечно, зависит от x, w и b,
  но для простоты я не стал обозначать эту зависимость.
  Обозначение ||v|| означает длину вектора v.
  Мы будем называть C квадратичной функцией стоимости;
  иногда её ещё называют среднеквадратичной ошибкой,
  или MSE.
  Если присмотреться к C, видно, что она не отрицательна,
  поскольку все члены суммы неотрицательны.
  Кроме того, стоимость C(w,b) становится малой,
  то есть, C(w,b) ? 0, именно тогда,
  когда y(x) примерно равна выходному вектору a
  у всех обучающих входных данных х.
  Так что наш алгоритм сработал хорошо,
  если сумел найти веса и смещения такие, что C(w,b) ? 0.
  И наоборот, сработал плохо, когда C(w,b) большая
  - это означает, что y(x) не совпадает с выходом
  для большого количества входных данных.
  Получается, цель обучающего алгоритма
  - минимизация стоимости C(w,b) как функции весов и смещений.
  Иначе говоря, нам нужно найти набор весов и смещений,
  минимизирующий значение стоимости.
  Мы будем делать это при помощи алгоритма под названием градиентный спуск.
  
  Зачем нам нужна квадратичная стоимость?
  Разве нас не интересует в основном
  количество правильно распознанных сетью изображений?
  Нельзя ли просто максимизировать это число напрямую,
  а не минимизировать промежуточное значение квадратичной стоимости?
  //// Очень интересный вопрос. И он не такой уж однозначный.
  Проблема в том,
  что количество правильно распознанных изображений
  не является гладкой функцией весов и смещений сети.
  По большей части, небольшие изменения весов и смещений
  не изменят количество правильно распознанных изображений.
  Из-за этого тяжело понять,
  как менять веса и смещения для улучшения эффективности.
  Если же мы будем использовать гладкую функцию стоимости,
  нам будет легко понять,
  как вносить небольшие изменения в веса и смещения,
  чтобы улучшать стоимость.
  Поэтому мы сначала сконцентрируемся на квадратичной стоимости,
  а потом уже изучим точность классификации.
  //// Т.е. в интерпретации этого автора все дело в "гладкости".
  //// А, может быть, дело в непрерывности процесса обучения,
  //// так как большую часть обучения "стоимость" будет не нулевой.
  //// А так при дискретном обучении, непонятно что делать
  //// при отсутствии ошибки?
  //// Т.е. это все опять "завязывается" на метод обучения,
  //// но ни как не привязан к решаемой задаче и структуре нейросети.
  
  Даже учитывая то, что мы хотим использовать гладкую функцию стоимости,
  вы всё равно можете интересоваться
  - почему мы выбрали квадратичную функцию для уравнения (6)?
  Разве её нельзя выбрать произвольно?
  Возможно, если бы мы выбрали другую функцию,
  мы бы получили совершенно другой набор минимизирующих весов и смещений?
  Разумный вопрос, и позднее мы вновь изучим функцию стоимости
  и внесём в неё некоторые правки.
  Однако квадратичная функция стоимости прекрасно работает
  для понимания базовых вещей в обучении НС,
  поэтому пока мы будем придерживаться её.
  //// Есть еще один аргумент в пользу "квадратичной функции стоимости",
  //// который автор не упоминает, может быть, считая самоочевидным.
  //// Квадратичная и, вообще, степенная функция обеспечивает
  //// как бы автоматическую "подстройку" шага изменения обучаемых весов
  //// к величине ошибки, теоретически существенно ускоряя обучение.
  //// Но опять же, наверняка, за счет чего-то другого.
  //// И хотелось бы точно понимать, что действительно ухудшает
  //// выбор такой "квадратичной зависимости".
  
  Подведём итог:
  наша цель в обучении НС сводится к поиску весов и смещений,
  минимизирующих квадратичную функцию стоимости C(w, b).
  Задача хорошо поставлена,
  однако пока что у неё есть много отвлекающих структур
  - интерпретация w и b как весов и смещений,
  притаившаяся на заднем плане функция ?,
  выбор архитектуры сети,
  MNIST и так далее.
  Оказывается, что мы очень многое можем понять,
  игнорируя большую часть этой структуры,
  и концентрируясь только на аспекте минимизации.
  Так что пока мы забудем об особой форме функции стоимости,
  связи с НС и так далее.
  Вместо этого мы собираемся представить,
  что у нас просто есть функция со многими переменными,
  и мы хотим её минимизировать.
  Мы разработаем технологию под названием градиентный спуск,
  которую можно использовать для решения таких задач.
  А потом мы вернёмся к определённой функции,
  которую хотим минимизировать для НС.
  
  Хорошо, допустим, мы пытаемся минимизировать некую функцию C(v).
  Это может быть любая функция
  с вещественными значениями от многих переменных v = v1, v2,:
  Заметьте, что я заменил запись w и b на v,
  чтобы показать, что это может быть любая функция
  - мы уже не зацикливаемся на НС.
  Полезно представлять себе, что у функции C есть только две переменных
  - v1 и v2:
  
  Мы бы хотели найти, где C достигает глобального минимума.
  
  ......
  
  Один из способов решения задачи - использовать алгебру
  для поиска минимума аналитическим путём.
  Мы можем вычислить производные и попытаться использовать их
  для поиска экстремума.
  Если нам повезёт, это сработает,
  когда С будет функцией от одной-двух переменных.
  Но при большом количестве переменных это превращается в кошмар.
  А для НС нам часто нужно гораздо больше переменных
  - у крупнейших НС функции стоимости сложным образом зависят
  от миллиардов весов и смещений.
  Использовать алгебру для минимизации этих функций не получится!
  
  .......
  
  Ладно, значит, алгебра не сработает.
  К счастью, существует прекрасная аналогия,
  предлагающая хорошо работающий алгоритм.
  Мы представляем себе нашу функцию чем-то вроде долины.
  С последним графиком это будет не так сложно сделать.
  И мы представляем себе шар, катящийся по склону долины.
  Наш опыт говорит нам, что шар в итоге скатится на самый низ.
  Возможно, мы можем использовать эту идею
  для поиска минимума функции?
  Мы случайным образом выберем начальную точку для воображаемого шара,
  а потом симулируем движение шара,
  как будто он скатывается на дно долины.
  Эту симуляцию мы можем использовать просто подсчитывая производные
  (и, возможно, вторые производные) С
  - они скажут нам всё о локальной форме долины,
  и, следовательно, о том, как наш шарик будет катиться.
  //// Все прекрасно в этой аналогии,
  //// кроме обоснования почему "ландшафтная парадигма"
  //// соответствует искомой задаче,
  //// а не какой-то "многоуровневый лабиринт",
  //// в котором такой "колобок" таким образом далеко не убежит.
  //// Т.е. вопрос в "неявной аксиоме" лежащей в основе такого подхода.
  
  ......
  
  ...давайте зададим вопрос:
  если бы нас назначили богом на один день,
  и мы создавали бы свои законы физики, говоря шару,
  как ему катиться
  какой закон или законы движения мы бы выбрали,
  чтобы шар всегда скатывался на дно долины?
  
  Чтобы уточнить вопрос, подумаем, что произойдёт,
  если мы передвинем шар на небольшое расстояние ?v1 в направлении v1,
  и на небольшое расстояние ?v2 в направлении v2.
  Алгебра говорит нам, что С меняется следующим образом:
  
  $ \Delta C \approx \frac{\partial C}{\partial v_1} \Delta v_1 + \frac{\partial C}{\partial v_2} \Delta v_2 \tag{7} $
  
  Мы найдем способ выбора таких ?v1 и ?v2, чтобы ?C была меньше нуля;
  то есть, мы будем выбирать их так,
  чтобы шар катился вниз.
  Чтобы понять, как это сделать, полезно определить ?v как вектор изменений,
  то есть ?v ? (?v1, ?v2)T,
  где Т - операция транспонирования,
  превращающая векторы-строки в векторы-столбцы.
  Мы также определим градиент С как вектор частных производных (?С/ ?v1, ?С/?v2)T.
  Обозначать градиентный вектор мы будем ?С:
  
  $ \nabla C \equiv (\frac{\partial C}{\partial v_1}, \frac{\partial C}{\partial v_2})^T \tag{8} $
  
  Скоро мы перепишем изменение ?C через ?v и градиент ?C.
  А пока я хочу прояснить кое-что, из-за чего люди часто подвисают на градиенте.
  Впервые встретившись с записью ?C,
  люди иногда не понимают, как они должны воспринимать символ ?.
  Что он конкретно означает?
  На самом деле можно спокойно считать ?С единым математическим объектом
  - определённым ранее вектором -
  который просто записывается с использованием двух символов.
  С этой точки зрения, ? - это как размахивать флагом,
  сообщающим о том, что "?С - это градиентный вектор".
  Существуют и более продвинутые точки зрения,
  с которых ? можно рассматривать,
  как независимую математическую сущность
  (к примеру, как оператор дифференцирования),
  однако нам они не понадобятся.
  
  С такими определениями выражение (7) можно переписать, как:
  
  $ \Delta C \approx \nabla C \cdot \Delta v \tag{9} $
  
  Это уравнение помогает объяснить,
  почему ?C называют градиентным вектором:
  он связывает изменения в v с изменениями С,
  именно так, как и ожидается от сущности под названием градиент.
  [англ. gradient - отклонение / прим. перев.]
  Однако более интересно, что это уравнение позволяет нам увидеть,
  как выбрать ?v так, чтобы ?C было отрицательным.
  
  ......
  
  Поэтому мы возьмём уравнение (10)
  для определения <закона движения> шара в нашем алгоритме градиентного спуска.
  То есть, мы будем использовать уравнение (10) для вычисления значения ?v,
  а потом будем двигать шар на это значение:
  
  $ v \rightarrow v' = v - \eta \nabla C \tag{11} $
  
  Потом мы снова применим это правило, для следующего хода.
  Продолжая повторение, мы будем понижать С,
  пока,
  как мы надеемся,
  не достигнем глобального минимума.
  
  Подытоживая, градиентный спуск работает
  через последовательное вычисление градиента ? C,
  и последующее смещение в противоположном направлении,
  что приводит к <падению> по склону долины.
  Визуализировать это можно так:
  
  .......
  
  Чтобы градиентный спуск работал правильно,
  нам нужно выбрать достаточно малое значение скорости обучения ?,
  чтобы уравнение (9) было хорошей аппроксимацией.
  В противном случае может получиться, что ?C > 0 - ничего хорошего!
  В то же время, не нужно, чтобы ? была слишком маленькой,
  поскольку тогда изменения ?v будут крохотными,
  и алгоритм будет работать слишком медленно.
  На практике ? меняется так,
  чтобы уравнение (9) давало неплохую аппроксимацию,
  и при этом алгоритм работал не слишком медленно.
  Позднее мы увидим, как это работает.
  
  Я объяснял градиентный спуск,
  когда функция С зависела только от двух переменных.
  Но всё работает так же, если С будет функцией от многих переменных.
  //// Есть ощущение, что в случае ОЧЕНЬ МНОГИХ переменных,
  //// какие-то "незначительные факторы", типа линеаризации,
  //// допущения о независимости самих переменных,
  //// отбрасывания производных высших порядков и др.,
  //// которыми можно пренебрегать при малом количестве переменных,
  //// становятся СУЩЕСТВЕННО ЗНАЧИМЕЙ с соответствующими последствиями.
  //// Но это так - мысли вслух.
  
  .......
  
  В каком-то смысле градиентный спуск можно считать
  оптимальной стратегией поиска минимума.
  Допустим, что мы пытаемся сдвинуться на ?v в позицию
  для максимального уменьшения С.
  Это эквивалентно минимизации ?C ? ?C ? ?v.
  Мы ограничим размер шага так, чтобы ||?v|| = ?
  для некоего малого постоянного ? > 0.
  Иначе говоря, мы хотим сдвинуться на небольшое расстояние
  фиксированного размера,
  и пытаемся найти направление движение,
  уменьшающее С так сильно, как это возможно.
  Можно доказать, что выбор ?v, минимизирующий ?C ? ?v,
  равняется ?v = -??C, где ? = ?/||?C||,
  определяется ограничением ||?v|| = ?.
  Так что градиентный спуск можно считать способом
  делать небольшие шаги в направлении,
  наиболее сильно уменьшающем С.
  
  ........
  
  Люди изучили множество вариантов градиентного спуска,
  включая и те, что более точно воспроизводят реальный физический шар.
  У таких вариантов есть свои преимущества,
  но и большой недостаток:
  необходимость вычисления вторых частных производных С,
  что может отнимать много ресурсов.
  Чтобы понять это, допустим, что нам надо вычислить
  все вторые частные производные ?2C / ?vj?vk.
  Если переменных vj миллион,
  тогда нам нужно вычислить примерно триллион (миллион в квадрате)
  вторых частных производных
  (на самом деле, полтриллиона, поскольку ?2C / ?vj?vk = ?2C / ?vk?vj.
  Но суть вы уловили).
  Это потребует много вычислительных ресурсов.
  Есть трюки, помогающие избежать этого,
  и поиск альтернатив градиентного спуска является
  областью активных исследований.
  Однако в этой книге мы будем использовать градиентный спуск и его варианты
  в качестве основного подхода к обучению НС.
  
  Как нам применить градиентный спуск к обучению НС?
  Нам нужно использовать его для поиска весов wk и смещений bl,
  минимизирующих уравнение стоимости (6).
  Давайте перезапишем правило обновления градиентного спуска,
  заменив переменные vj весами и смещениями.
  Иначе говоря, теперь у нашей <позиции> есть компоненты wk и bl,
  а у градиентного вектора ?C есть соответствующие компоненты ?C/?wk и ?C/?bl.
  Записав наше правило обновления с новыми компонентами, мы получим:
  
  ......
  
  Повторно применяя это правило обновления, мы можем <катиться под горку>,
  и, если повезёт, найти минимум функции стоимости.
  Иначе говоря, это правило можно использовать для обучения НС.
  
  Применению правила градиентного спуска есть несколько препятствий.
  Подробнее мы изучим их в следующих главах.
  Но пока я хочу упомянуть только одну проблему.
  Чтобы понять её, давайте вернёмся к квадратичной стоимости в уравнении (6).
  Заметьте, что эта функция стоимости выглядит, как C = 1/n ?x Cx,
  то есть это среднее по стоимости Cx ? (||y(x)?a||2)/2
  для отдельных обучающих примеров.
  На практике для вычисления градиента ?C нам нужно вычислять градиенты ?Cx
  отдельно для каждого обучающего входа x,
  а потом усреднять их, ?C = 1/n ?x ?Cx.
  К сожалению, когда количество входных данных будет очень большим,
  это займёт очень много времени,
  и такое обучение будет проходить медленно.
  
  Для ускорения обучения можно использовать стохастический градиентный спуск.
  Идея в том, чтобы приблизительно вычислить градиент ?C,
  вычислив ?Cx для небольшой случайной выборки обучающих входных данных.
  Посчитав их среднее, мы можем быстро получить
  хорошую оценку истинного градиента ?C,
  и это помогает ускорить градиентный спуск,
  и, следовательно, обучение.
  //// Вот тут, честно признаюсь, в данный момент у меня какое-то непонимание,
  //// чем отличается "правильный градиентный спуск" от "стохастического".
  //// Что ж придется разбираться. Может быть, надо и с Copilot проконсультироваться.
  
  //// Смысл моего недопонимания "правильного градиентного спуска" в том,
  //// что в моем понимании это классический "сферический конь в вакууме".
  //// Т.е. красивая концепция не имеющая в данной задаче практического смысла.
  //// И дело даже не в том, что нужно на каждом шаге "спуска" делать
  //// колоссальные вычисления по всем обучающим данным,
  //// включая еще, возможно, не известные при "дообучении",
  //// о чем говорит автор.
  
  //// Дело в "ландшафтной концепции" представления задачи,
  //// для которой пытаются применить градиентный спуск.
  //// Сама по себе "ландшафтная концепция" предполагает какой-то
  //// ФИКСИРОВАННЫЙ "ландшафт" решения задачи.
  //// Одновременно с этим вычисление частных производных градиента
  //// предполагает фиксированность большей части весов при этом вычислении.
  //// Иначе "расчетчики сойдут с ума" в попытке учесть все возможные
  //// последствия изменений.
  //// Это, надеюсь, достаточно понятно.
  
  //// А вот то, что "ландшафт" задачи обучения весов нейросети
  //// является отнюдь не фиксированным, а "зыбучим",
  //// изменяющимся при каждом новом обучающем примере,
  //// уже далеко не так очевидно.
  //// Это даже без различных нелинейностей в активационных функциях нейронов.
  //// Тут лучше бы работала "концепция лабиринта",
  //// в котором есть еще и "открывающиеся/закрывающиеся порталы",
  //// при прохождении определенного отрезка маршрута.
  
  //// Связано это, в первую очередь, со сложной структурой значений
  //// элементов эмбеддинга и самим процессом обучения.
  //// И это отдельный и очень длинный разговор. Если интересно, см.:
  //// "Металоция неведомого. Модуль Э. Эмбеддинги."
  
  //// А в данном контексте важно понимать,
  //// что ЛЮБОМ изменении весов нейронов "ландшафт" может необратимо измениться,
  //// и вычислять "правильный градиент" СМЫСЛА НЕТ.
  //// Собственно этого никто "находясь в здравом уме и трезвой памяти"
  //// делать даже не пытается, а молчаливо используют "стохастический градиент".
  //// А "правильный градиент" используют только в "умных текстах".
  
  Формулируя более точно, стохастический градиентный спуск
  работает через случайную выборку
  небольшого количества m обучающих входных данных.
  Мы назовём эти случайные данные X1, X2,.., Xm,
  и назовём их мини-пакетом.
  Если размер выборки m будет достаточно большим,
  среднее значение ?CXj будет достаточно близким к среднему по всем ?Cx,
  то есть
  
  $ \frac{\sum^m_{j=1} \nabla C_{X_j}}{m} \approx \frac{\sum_x \nabla C_x}{n} = \nabla C \tag{18} $
  
  где вторая сумма идёт по всему набору обучающих данных.
  Поменяв части местами, мы получим
  
  $ \nabla C \approx \frac{1}{m} \sum^m_{j=1} \nabla C_{X_j} \tag{19} $
  
  что подтверждает, что мы можем оценить общий градиент,
  вычислив градиенты для случайно выбранного мини-пакета.
  
  Чтобы связать это непосредственно с обучением НС,
  допустим, что wk и bl обозначают веса и смещения нашей НС.
  Тогда стохастический градиентный спуск выбирает
  случайный мини-пакет входных данных, и обучается на них
  
  $ w_k \rightarrow w'_k = w_k - \frac{\eta}{m} \sum_j \frac{\partial C_{X_j}}{\partial w_k} \tag{20} $
  
  $ b_l \rightarrow b'_l = b_l - \frac{\eta}{m} \sum_j \frac{\partial C_{X_j}}{\partial b_l} \tag{21} $
  
  где идёт суммирование по всем обучающим примерам Xj в текущем мини-пакете.
  Затем мы выбираем ещё один случайный мини-пакет и обучаемся на нём.
  И так далее, пока мы не исчерпаем все обучающие данные,
  что называется окончанием обучающей эпохи.
  В этот момент мы начинаем заново новую эпоху обучения.
  //// Интересно, есть какие-то исследования по теме оптимального выбора
  //// размера "мини-пакета"?
  
  Кстати, стоит отметить,
  что соглашения по поводу масштабирования функции стоимости и обновлений весов
  и смещений мини-пакетом разнятся.
  В уравнении (6) мы масштабировали функцию стоимости в 1/n раз.
  Иногда люди опускают 1/n,
  суммируя стоимости отдельных обучающих примеров,
  вместо вычисления среднего.
  Это полезно, когда общее количество обучающих примеров заранее неизвестно.
  Такое может случиться,
  например, когда дополнительные данные появляются в реальном времени.
  Таким же образом правила обновления мини-пакета (20) и (21)
  иногда опускают член 1/m перед суммой.
  Концептуально это ни на что не влияет,
  поскольку это эквивалентно изменению скорости обучения ?.
  Однако на это стоит обращать внимание при сравнении различных работ.
  
  Стохастический градиентный спуск можно представлять себе,
  как политическое голосование:
  гораздо проще взять выборку в виде мини-пакета,
  чем применить градиентный спуск к полной выборке
  - точно так же, как опрос на выходе из участка провести проще,
  чем провести полноценные выборы.
  К примеру, если наш обучающий набор имеет размер n = 60 000, как MNIST,
  и мы сделаем выборку мини-пакета размером m = 10,
  то в 6000 раз ускорит оценку градиента!
  Конечно, оценка не будет идеальной
  - в ней будет статистическая флуктуация -
  но ей и не надо быть идеальной:
  нам лишь надо двигаться в примерно том направлении,
  которое уменьшает С,
  а это значит, что нам не нужно точно вычислять градиент.
  На практике стохастический градиентный спуск
  - распространённая и мощная техника обучения НС,
  и база большинства обучающих технологий,
  которые мы разработаем в рамках книги.
  
  .......
  
  Реализация сети для классификации цифр
  
  .......
  
  Из этого стоит вынести урок о том,
  что отладка НС не является тривиальной задачей,
  и это, как и обычное программирование, частью искусство.
  //// Программирование это не искусство - это инженерная дисциплина.
  //// Причем с ударением на оба слова сразу,
  //// а "дисциплина" строго по классику - "как осознанная необходимость".
  //// Это я за 45 лет выучил на собственной шкуре,
  //// хотя тоже начинал с "классики",
  //// с того самого трехтомника Кнута "Искусство программирования".
  Вы должны обучиться этому искусству отладки,
  //// Это тоже должна быть "дисциплина", а не "искусство"
  //// и, тем более "шаманство".
  чтобы получать хорошие результаты от НС.
  В целом, нам необходимо выработать эвристику
  для выбора хороших гиперпараметров и хорошей архитектуры.
  
  .......
  
  Обычно программисты считают,
  что для решения подобной сложной задачи
  требуется сложный алгоритм.
  Но даже НС в работе-рекордсмене используют довольно простые алгоритмы,
  являющиеся небольшими вариациями тех, что мы рассмотрели в этой главе.
  Вся сложность автоматически появляется во время обучения
  на основе обучающих данных.
  В каком-то смысле, мораль наших результатов и тех,
  что содержатся в более сложных работах, в том, что для некоторых задач
  
  сложный алгоритм <= простой алгоритм обучения + хорошие обучающие данные
  
  К глубокому обучению
  
  Хотя наша сеть демонстрирует впечатляющую эффективность,
  достигается она загадочным способом.
  Веса и смешения сети автоматически обнаруживаются.
  Значит, у нас нет готового объяснения того,
  как сеть делает то, что делает.
  Можно ли найти какой-то способ понять базовые принципы
  классификации сетью рукописных цифр?
  И можно ли, учитывая их, улучшить результат?
  
  Переформулируем эти вопросы более жёстко:
  допустим, что через несколько десятилетий НС превратятся
  в искусственный интеллект (ИИ).
  Поймём ли мы, как этот ИИ работает?
  Возможно, сети останутся непонятными для нас,
  с их весами и смещениями, поскольку они назначаются автоматически.
  В ранние годы исследований ИИ люди надеялись,
  что попытки создания ИИ также помогут нам понять принципы,
  лежащие в основе интеллекта, и, может быть,
  даже работы человеческого мозга.
  Однако в итоге может оказаться,
  что мы не будем понимать ни мозг,
  ни то, как работает ИИ!
  
  Чтобы разобраться с этими вопросами,
  давайте вспомним об интерпретации искусственных нейронов,
  данной мною в начале главы
  - что это способ взвешивать свидетельства.
  Допустим, мы хотим определить, есть ли на изображении лицо человека:
  
  К этой проблеме можно подходить так же,
  как к распознаванию рукописного ввода:
  используя пиксели изображений в качестве входных данных для НС,
  а на выходе НС будет один нейрон, который будет говорить,
  <Да, это лицо>, или <Нет, это не лицо>.
  
  Допустим, мы сделаем это, но не используя алгоритм обучения.
  Мы попробуем вручную создать сеть,
  выбирая подходящие веса и смещения.
  Как мы можем к этому подступиться?
  Забыв на минуту о НС, мы могли бы разбить задачу на подзадачи:
  есть ли у изображения глаз в левом верхнем углу?
  Есть ли глаз в правом верхнем углу?
  Есть ли посередине нос?
  Есть ли внизу посередине рот?
  Есть ли вверху волосы? И так далее.
  
  Если ответы на несколько из этих вопросов положительные,
  или даже <вероятно, да>,
  тогда мы заключаем, что на изображении, возможно, есть лицо.
  И наоборот, если ответы отрицательные, то лица, вероятно, нет.
  
  Это, конечно, примерная эвристика,
  и у неё множество недостатков.
  Возможно, это лысый человек, и у него нет волос.
  Возможно, нам видна только часть лица, или лицо под углом,
  так что некоторые части лица закрыты.
  И всё же эвристика говорит о том, ч
  то если мы можем решить подзадачи при помощи нейросетей,
  то, возможно, мы можем создать НС для распознавания лиц,
  комбинируя сети для подзадач.
  Ниже приведена возможная архитектура такой сети,
  в которой прямоугольниками обозначены подсети.
  Это не совсем реалистичный подход к решению задачи распознавания лиц:
  он нужен, чтобы помочь нам интуитивно разобраться в работе нейросетей.
  
  В прямоугольниках - подзадачи:
  есть ли у изображения глаз в левом верхнем углу?
  Есть ли глаз в правом верхнем углу?
  Есть ли посередине нос?
  Есть ли внизу посередине рот?
  Есть ли вверху волосы?
  И так далее.
  
  Возможно, что и подсети тоже можно разобрать на составляющие.
  Возьмём вопрос с наличием глаза в левом верхнем углу.
  Его можно разобрать на вопросы типа:
  <Есть ли бровь?>, <Есть ли ресницы?>, <Есть ли зрачок?> и так далее.
  Конечно, в вопросах должна содержаться информация о расположении
  - <Расположена ли бровь слева вверху, над зрачком?>, и так далее -
  но давайте пока упростим.
  Поэтому сеть, отвечающую на вопрос о наличии глаза,
  можно разобрать на составляющие:
  
  <Есть ли бровь?>, <Есть ли ресницы?>, <Есть ли зрачок?>
  
  Эти вопросы можно дальше разбивать на мелкие,
  по шагам через многие слои.
  В итоге мы будем работать с подсетями,
  отвечающие на такие простые вопросы,
  что их легко можно разобрать на уровне пикселей.
  Эти вопросы могут касаться,
  к примеру, наличия или отсутствия простейших форм
  в определённых местах изображения.
  На них смогут ответить отдельные нейроны, связанные с пикселями.
  
  В итоге получится сеть, разбивающая очень сложные вопросы
  - наличие на изображении лица -
  на очень простые вопросы,
  на которые можно отвечать на уровне отдельных пикселей.
  Она будет делать это через последовательность из многих слоёв,
   в которых первые слои отвечают
  на очень простые и конкретные вопросы об изображении,
  а последние создают иерархию из более сложных и абстрактных концепций.
  Сети с такой многослойной структурой
  - двумя или большим количеством скрытых слоёв -
  называются глубокими нейросетями (ГНС).
  
  Конечно, я не говорил о том,
  как сделать это рекурсивное разбиение по подсетям.
  //// А это самое главное. То чего сейчас вообще непонятно как сделать.
  Определённо непрактично будет вручную подбирать веса и смещения.
  Мы бы хотели использовать обучающие алгоритмы,
  чтобы сеть автоматически обучалась весам и смещениям
  - а через них и иерархии концепций -
  на основе обучающих данных.
  //// Цель заманчивая, но почему-то думается, что в основе такой технологии
  //// будет не только сеть другой конструкции, но и другой механизм обучения,
  //// в котором если и будет "градиентный спуск", то далеко не на ведущей роли.
  Исследователи в 1980-х и 1990-х пытались
  использовать стохастический градиентный спуск и обратное распространение
  для тренировки ГНС.
  К сожалению, за исключением нескольких особых архитектур,
  у них ничего не получилось.
  Сети обучались, но очень медленно,
  и на практике это было слишком медленно для того,
  чтобы это можно было как-то использовать.
  
  С 2006 года было разработано несколько технологий,
  позволяющих обучать ГНС.
  Они основаны на стохастическом градиентном спуске и обратном распространении,
  но содержат и новые идеи.
  Они позволили обучать гораздо более глубокие сети
  - сегодня люди спокойно обучают сети с 5-10 слоями.
  //// А надо как минимум на два порядка больше,
  //// если замахиваться на описанный выше подход.
  И оказывается, что они гораздо лучше решают многие проблемы,
  чем неглубокие НС,
  то есть сети с одним скрытым слоем.
  Причина, конечно, в том, что ГНС могут создавать
  сложную иерархию концепций.
  Это похоже на то,
  как языки программирования используют модульные схемы и идеи абстракций,
  чтобы на них можно было создавать сложные компьютерные программы.
  Сравнивать глубокую НС с неглубокой НС
  - примерно как сравнивать язык программирования,
  умеющий делать вызовы функций,
  с языком, этого не умеющим.
  Абстракция в НС выглядит не так, как в языках программирования,
  но имеет такую же важность.
  
  .........
  
  //// Конец цитирования.
  
  Какой главный вывод можно сделать из этого материала?
  что существующий "триумфальный симбиоз"
  "сигмоидного нейрона", "градиентного спуска", "обратного распространения",
  "квадратичной ошибки" и, наверно, еще чего-то, что осталось вне поля зрения,
  таят в себе целый комплекс и "неявных аксиом" и "зародышей проблем".
  
  И, самое главное, не позволяют сделать практически никаких предсказаний
  о результатах проектирования новой конкретной модели.
  Т.е. нынешняя "теория машинного обучения"
  пока не является "инженерной дисциплиной".
  
  
  =========
  
  24.03.2024 15:40
  
  Погружение в математику "обратного распространения".
  В оглавление.
  
  Продолжаем погружаться в глубины "глубокого обучения".
  Возможно, это самый трудный материал в этом модуле
  с точки зрения обилия "математической абракадабры".
  Но ведь можно смотреть не на запись формул,
  а на их содержание/интерпретацию,
  чтобы понять не только используемые идеи/концепции,
  но сопутствующие им возможности и ограничения.
  
  И именно с этой точки зрения предлагаемый материал
  существенно отличается от десятков, а может и сотен подобных.
  Поэтому предлагаю поглубже вдохнуть,
  открыть в соседнем окне сам материал, чтобы видеть оригинальные иллюстрации,
  кстати достаточно информативные.
  И спокойно нырнуть в таинственные глубины
  математики "обратного распространения ошибки".
  Нет там ничего особенно головоломного, чтобы заранее пугаться:
  
  "Нейросети и глубокое обучение,
  глава 2: как работает алгоритм обратного распространения".
  Автор: SLY_G (Вячеслав Голованов)
  https://habr.com/ru/articles/457980/.
  1 июл 2019 в 10:00
  
  Автор оригинала: Michael Nielsen
  http://neuralnetworksanddeeplearning.com/chap2.html
  
  //// Начало цитирования.
  
  В прошлой главе мы видели,
  как нейросети могут самостоятельно обучаться весам и смещениям
  с использованием алгоритма градиентного спуска.
  Однако в нашем объяснении имелся пробел:
  мы не обсуждали подсчёт градиента функции стоимости.
  А это приличный пробел!
  В этой главе я расскажу быстрый алгоритм для вычисления подобных градиентов,
  известный, как обратное распространение.
  
  Впервые алгоритм обратного распространения придумали в 1970-х,
  но его важность не была до конца осознана вплоть до знаменитой работы 1986 года,
  которую написали Дэвид Румельхарт, Джоффри Хинтон и Рональд Уильямс.
  В работе описано несколько нейросетей,
  в которых обратное распространение работает гораздо быстрее,
  чем в более ранних подходах к обучению,
  из-за чего с тех пор можно было использовать нейросеть
  для решения ранее неразрешимых проблем.
  Сегодня алгоритм обратного распространения
  - рабочая лошадка обучения нейросети.
  
  Эта глава содержит больше математики, чем все остальные в книге.
  Если вам не особенно по нраву математика,
  у вас может возникнуть искушение пропустить эту главу
  и просто относиться к обратному распространению,
  как к чёрному ящику,
  подробности работы которого вы готовы игнорировать.
  Зачем тратить время на их изучение?
  
  Причина, конечно, в понимании.
  В основе обратного распространения лежит
  выражение частной производной dC / dw
  функции стоимости C по весу w (или смещению b) сети.
  Выражение показывает, насколько быстро меняется стоимость
  при изменении весов и смещений.
  И хотя это выражение довольно сложное,
  у него есть своя красота,
  ведь у каждого его элемента есть естественная и интуитивная интерпретация.
  Поэтому обратное распространение
  - не просто быстрый алгоритм для обучения.
  Он даёт нам подробное понимание того,
  как изменение весов и смещений меняет всё поведение сети.
  А это стоит того, чтобы изучить подробности.
  
  Учитывая всё это, если вы хотите просто пролистать эту главу
  или перепрыгнуть к следующей, ничего страшного.
  Я написал остальную книгу так,
  чтобы она была понятной,
  даже если считать обратное распространение чёрным ящиком.
  Конечно, позднее в книге будут моменты,
  с которых я делаю отсылки к результатам этой главы.
  Но в тот момент вам должны быть понятны основные заключения,
  даже если вы не отслеживали все рассуждения.
  //// Ну, а я в этом месте могу сказать,
  //// что перескочить через эту главу, значит расписаться в том,
  //// что не сделал даже попытки ПОНЯТЬ МАГИЮ обучения нейросетей.
  
  Для разогрева:
  быстрый матричный подход вычисления выходных данных нейросети
  
  Перед обсуждением обратного распространения,
  давайте разогреемся быстрым матричным алгоритмом
  для вычисления выходных данных нейросети.
  Мы вообще-то уже встречались с этим алгоритмом к концу предыдущей главы,
  но я описал его быстро, поэтому его стоит заново рассмотреть подробнее.
  В частности, это будет хороший способ приспособиться к записи,
  используемой в обратном распространении, но в знакомом контексте.
  
  Начнём с записи, позволяющей нам недвусмысленно обозначать веса в сети.
  Мы будем использовать wljk для обозначения веса связи нейрона ?k
  в слое ?(l-1) с нейроном ?j в слое ?l.
  Так, к примеру, на диаграмме ниже показан
  вес связи четвёртого нейрона второго слоя со вторым нейроном третьего слоя:
  
  Сначала такая запись кажется неуклюжей,
  и требует некоторых усилий на привыкание.
  Однако вскоре она покажется вам простой и естественной.
  Одна её особенность - порядок индексов j и k.
  Вы могли бы решить, что разумнее было бы использовать j
  для обозначения входного нейрона, а k - для выходного,
  а не наоборот, как у нас.
  Причину такой особенности я объясню ниже.
  
  Сходные обозначения мы будем использовать для смещений и активаций сети.
  В частности, blj будет обозначать смещение нейрона ?j в слое ?l.
  alj будет обозначать активацию нейрона ?j в слое ?l.
  На следующей диаграмме показаны примеры использования этой записи:
  
  С такой записью активация alj нейрона ?j в слое ?l
  связана с активацией в слое ?(l-1) следующим уравнением
  (сравните с уравнением (4) и его обсуждением в прошлой главе):
  
  $ a^l_j = \sigma (\sum_k w^l_{jk} a^{l?1}_k + b^l_j) \tag{23} $
  
  где сумма идёт по всем нейронам k в слое (l-1).
  Чтобы перезаписать это выражение в матричном виде,
  мы определим матрицу весов wl для каждого слоя l.
  Элементы матрицы весов - это просто веса,
  соединённые со слоем ?l,
  то есть, элемент в строке ?j и столбце ?k будет wljk.
  Сходным образом для каждого слоя l мы определяем вектор смещения bl.
  Вы, наверное, догадались, как это работает
  - компонентами вектора смещения будут просто значения blj,
  по одному компоненту для каждого нейрона в слое ?l.
  И, наконец, мы определим вектор активации al,
  компонентами которого будут активации alj.
  
  Последним ингредиентом, необходимым для того,
  чтобы перезаписать (23), будет матричная форма векторизации функции ?.
  С векторизацией мы вскользь столкнулись в прошлой главе
  - идея в том, что мы хотим применить функцию ? к каждому элементу вектора v.
  Мы используем очевидную запись ?(v) для обозначения
  поэлементного применения функции.
  То есть, компонентами ?(v) будут просто ?(v)j = ?(vj).
  Для примера, если у нас есть функция f(x) = x2,
  то векторизованная форма f даёт нам
  
  $ f( \begin{bmatrix} 2\\ 3 \end{bmatrix} ) = \begin{bmatrix} f(2)\\ f(3) \end{bmatrix} = \begin{bmatrix} 4\\ 9 \end{bmatrix} \tag{24} $
  
  то есть, векторизованная f просто возводит в квадрат каждый элемент вектора.
  
  Учитывая все эти формы записи, уравнение (23) можно переписать
  в красивой и компактной векторизованной форме:
  
  $ a^l = \sigma(w^l a^{l?1} + b^l) \tag{25} $
  
  Такое выражение позволяет нам более глобально взглянуть
  на связь активаций одного слоя с активациями предыдущего:
  мы просто применяем матрицу весов к активациям,
  добавляем вектор смещения и потом применяем сигмоиду.
  Кстати, именно эта запись и требует использования записи wljk.
  Если бы мы использовали индекс j для обозначения входного нейрона,
  а k для выходного, нам пришлось бы заменить матрицу весов в уравнении (25)
  на транспонированную.
  Это небольшое, но раздражающее изменение,
  и мы бы потеряли простоту заявления (и размышления)
  о <применении матрицы весов к активациям>.
  Такой глобальный подход проще и лаконичнее
  (и использует меньше индексов!),
  //// Но больше умственной энергии на понимание/привыкание.
  чем понейронный.
  Это просто способ избежать индексного ада,
  не теряя точности обозначения происходящего.
  Также это выражение полезно на практике,
  поскольку большинство матричных библиотек предлагают
  быстрые способы перемножения матриц,
  сложения векторов и векторизации.
  Код в прошлой главе непосредственно пользовался этим выражением
  для вычисления поведения сети.
  
  Используя уравнение (25) для вычисления al,
  мы вычисляем промежуточное значение zl ? wlal?1+bl.
  Эта величина оказывается достаточно полезной для именования:
  мы называем zl взвешенным входом нейронов слоя ?l.
  Позднее мы активно будем использовать этот взвешенный вход.
  Уравнение (25) иногда записывают через взвешенный вход,
  как al = ?(zl).
  Стоит также отметить, что у zl есть компоненты
  $ z^l_j = \sum_k w^l_{jk} a^{l?1}_k + b^l_j $,
  то есть, zlj - это всего лишь
  взвешенный вход функции активации нейрона j в слое l.
  //// Интересно, это значение "взвешенного входа" при работе/обучении нейросетей
  //// где хранится?
  //// Или это нужно только для понимания последующих формул?
  
  Два необходимых предположения по поводу функции стоимости
  
  Цель обратного распространения -
  вычислить частные производные ?C/?w и ?C/?b функции стоимости C
  для каждого веса w и смещения b сети.
  Чтобы обратное распространение сработало,
  нам необходимо сделать два главных предположения
  по поводу формы функции стоимости.
  Однако перед этим полезно будет представлять себе пример функции стоимости.
  Мы используем квадратичную функцию из прошлой главы (уравнение (6)).
  В записи из предыдущего раздела она будет иметь вид
  
  $ C = \frac{1}{2n}\sum_x ||y(x) ? a^L(x)||^2 \tag{26} $
  
  где: n - общее количество обучающих примеров;
  сумма идёт по всем примерам x;
  y=y(x) - необходимые выходные данные;
  L обозначает количество слоёв в сети;
  aL = aL (x) - вектор выхода активаций сети, когда на входе x.
  
  Ладно, так какие нам нужны предположения касательно функции стоимости С,
  чтобы применять обратное распространение?
  Первое - функцию стоимости можно записать
  как среднее C = 1/n ?x Cx функций стоимости Cx
  для отдельных обучающих примеров x.
  Это выполняется в случае квадратичной функции стоимости,
  где стоимость одного обучающего примера Cx = 1/2 ||y ? aL||2.
  Это предположение будет верным и для всех остальных функций стоимости,
  которые встретятся нам в книге.
  //// Стоит запомнить этот момент.
  //// Чем-то это напоминает "среднюю температуру по палате".
  //// Вряд ли это не имеет каких-то "отрижительных" и/или "полоцательных" следствий.
  //// И это точно связано с концепцией "фиксированного ландшафта" решения.
  
  Это предположение нужно нам потому,
  что на самом деле обратное распространение позволяет нам вычислять
  частные производные ?C/?w и ?C/?b,
  усредняя по обучающим примерам.
  Приняв это предположение, мы предположим,
  что обучающий пример x фиксирован,
  и перестанем указывать индекс x,
  записывая стоимость Cx как C.
  Потом мы вернём x, но пока что лучше его просто подразумевать.
  
  Второе предположение касательно функции стоимости
  - её можно записать как функцию выхода нейросети:
  
  К примеру, квадратичная функция стоимости удовлетворяет этому требованию,
  поскольку квадратичную стоимость одного обучающего примера x можно записать,
  как
  
  $ C = 1/2 || y?a^L ||^2 = 1/2 \sum_j (y_j ? a^L_j)^2 \tag{27} $
  
  что и будет функцией выходных активаций.
  Конечно, эта функция стоимости также зависит от желаемого выхода y,
  и вы можете удивиться, почему мы не рассматриваем C как функцию ещё и от y.
  Однако вспомним, что входной обучающий пример x фиксирован,
  поэтому выход y тоже фиксирован.
  В частности, его мы не можем изменить, меняя веса и смещения,
  то есть, это не то, что выучивает нейросеть.
  Поэтому имеет смысл считать C функцией от только выходных активаций aL,
  а y - просто параметром, помогающим её определять.
  //// Не очень, на мой взгляд, внятная формулировка.
  //// Во всяком случае, я не понял вот этого
  //// "не можем изменить, меняя веса и смещения".
  //// Кажется, что что-то важное упускаю.
  
  //// Может быть тот случай, когда "выход y" представляет собой
  //// "координату", или "вероятность", или "интенсивность"
  //// или еще что-то МНОГОЗНАЧНОЕ.
  //// И тогда используемые в данном материале рассуждения,
  //// могут "не совсем" соответствовать таким случаям,
  //// и выводимые общие формулы "обратного распространения",
  //// возможно, оказываются не совсем "общими".
  
  //// Но моей математической подготовки и понимания этого параграфа
  //// явно не хватает, чтобы это как-то раскрыть/осмыслить.
  //// Но, на всякий случай, постараюсь запомнить этот момент.
  
  Произведение Адамара s?t
  
  Алгоритм обратного распространения основан
  на обычных операциях линейной алгебры
  - сложении векторов, умножении вектора на матрицу, и т.д.
  Однако одна из операций используется менее часто.
  Допустим, s и t - два вектора одной размерности.
  Тогда через s?t мы обозначим поэлементное перемножение двух векторов.
  Тогда компоненты s?t будут просто (s?t)j = sjtj. Например:
  
  $ \begin{bmatrix} 1\\ 2 \end{bmatrix} \odot \begin{bmatrix} 3\\ 4 \end{bmatrix} = \begin{bmatrix} 1 * 3\\ 2 * 4 \end{bmatrix} = \begin{bmatrix} 3\\ 8 \end{bmatrix} \tag{28} $
  
  Такое поэлементное произведение иногда называют произведением Адамара
  или произведением Шура.
  Мы будем называть его произведением Адамара.
  Хорошие библиотеки для работы с матрицами обычно имеют
  быструю реализацию произведения Адамара,
  и это бывает удобно при реализации обратного распространения.
  
  Четыре фундаментальных уравнения в основе обратного распространения
  
  Обратное распространение связано с пониманием того,
  как изменение весов и смещений сети меняет функцию стоимости.
  По сути, это означает подсчёт частных производных ?C/?wljk и ?C/?blj.
  Но для их вычисления сначала мы вычисляем промежуточное значение ?lj,
  которую мы называем ошибкой в нейроне ?j в слое ?l.
  Обратное распространение даст нам процедуру для вычисления ошибки ?lj,
  а потом свяжет ?lj с ?C/?wljk и ?C/?blj.
  
  Чтобы понять, как определяется ошибка,
  представьте, что в нашей нейросети завёлся демон:
  
  Он сидит на нейроне ?j в слое ?l.
  При поступлении входных данных демон нарушает работу нейрона.
  Он добавляет небольшое изменение ?zlj к взвешенному входу нейрона,
  и вместо того, чтобы выдавать ?(zlj), нейрон выдаст ?(zlj + ?zlj).
  Это изменение распространится и через следующие слои сети,
  что в итоге изменит общую стоимость на (?C/?zlj) * ?zlj.
  
  Но наш демон хороший, и он пытается помочь вам улучшить стоимость,
  то есть, найти ?zlj, уменьшающее стоимость.
  Допустим, значение ?C/?zlj велико (положительное или отрицательное).
  Тогда демон может серьёзно уменьшить стоимость,
  выбрав ?zlj со знаком, противоположным ?C/?zlj.
  А если же ?C/?zlj близко к нулю,
  тогда демон не может сильно улучшить стоимость, меняя взвешенный вход zlj.
  Так что, с точки зрения демона, нейрон уже близок к оптимуму
  (это, конечно, верно только для малых ?zlj.
  Допустим, таковы ограничения действий демона).
  Поэтому в эвристическом смысле ?C/?zlj является мерой ошибки нейрона.
  
  Под мотивацией от этой истории определим ошибку ?lj нейрона j в слое l, как
  
  $ \delta l_j \equiv \frac{\partial C}{\partial z^l_j} \tag{29} $
  
  По обычному нашему соглашению мы используем ?l для обозначения вектора ошибок,
  связанного со слоем l.
  Обратное распространение даст нам способ подсчитать ?l для любого слоя,
  а потом соотнести эти ошибки с теми величинами,
  которые нас реально интересуют, ?C/?wljk и ?C/?blj.
  
  Вас может интересовать, почему демон меняет взвешенный вход zlj.
  Ведь было бы естественнее представить,
  что демон изменяет выходную активацию alj,
  чтобы мы использовали ?C/?alj в качестве меры ошибки.
  На самом деле, если сделать так,
  то всё получается очень похожим на то,
  что мы будем обсуждать дальше.
  Однако в этом случае представление обратного распространения
  будет алгебраически чуть более сложным.
  Поэтому мы остановимся на варианте ?lj = ?C/?zlj в качестве меры ошибки.
  
  В задачах классификации термин <ошибка> иногда означает
  количество неправильных классификаций.
  К примеру, если нейросеть правильно классифицирует 96,0% цифр,
  то ошибка будет равна 4,0%.
  Очевидно, это совсем не то, что мы имеем в виду под векторами ?.
  Но на практике обычно можно без труда понять,
  какое значение имеется в виду.
  
  План атаки:
  обратное распространение основано на четырёх фундаментальных уравнениях.
  Совместно они дают нам способ вычислить как ошибку ?l,
  так и градиент функции стоимости.
  Я привожу их ниже.
  Не нужно ожидать их мгновенного освоения.
  Вы будете разочарованы.
  Уравнения обратного распространения настолько глубоки,
  что для хорошего их понимания требуется ощутимое время и терпение,
  и постепенное углубление в вопрос.
  Хорошие новости в том, что это терпение окупится сторицей.
  Поэтому в данном разделе наши рассуждения только начинаются,
  помогая вам идти по пути глубокого понимания уравнений.
  
  Вот схема того, как мы будем углубляться в эти уравнения позже:
  я дам их краткое доказательство,
  помогающее объяснить, почему они верны;
  мы перепишем их в алгоритмической форме в виде псевдокода,
  и увидим, как реализовать его в реальном коде на python;
  в последней части главы мы выработаем интуитивное представление
  о значении уравнений обратного распространения,
  и о том, как их можно найти с нуля.
  Мы будем периодически возвращаться к четырём фундаментальным уравнениям,
  и чем глубже вы будете их понимать,
  тем более комфортными, и возможно, красивыми и естественными
  они будут вам казаться.
  
  Уравнение ошибки выходного слоя, ?L: компоненты ?L считаются, как
  
  $ \delta^L_j = \frac{\partial C}{\partial a^L_j} \sigma' (z^L_j) \tag{BP1} $
  
  Очень естественное выражение.
  Первый член справа, ?C / ?aLj, измеряет,
  насколько быстро стоимость меняется как функция выходной активации ?j.
  Если, к примеру, C не особенно зависит от конкретного выходного нейрона j,
  тогда ?Lj будет малым, как и ожидается.
  Второй член справа, ?'(zLj), измеряет,
  насколько быстро функция активации ? меняется в zLj.
  
  Заметьте, что всё в (BP1) легко подсчитать.
  В частности, мы вычисляем zLj при подсчёте поведения сети,
  и на вычисление ?'(zLj) уйдёт незначительно больше ресурсов.
  Конечно, точная форма ?C / ?aLj зависит от формы функции стоимость.
  Однако, если функция стоимости известна,
  то не должно быть проблем с вычислением ?C / ?aLj.
  К примеру, если мы используем квадратичную функцию стоимости,
  тогда C = 1/2 ?j (yj ? aLj)2,
  поэтому ?C / ?aLj = (aLj ? yj), что легко подсчитать.
  
  Уравнение (BP1) - это покомпонентное выражение ?L.
  Оно совершенно нормальное, но не записано в матричной форме,
  которая нужна нам для обратного распространения.
  Однако, его легко переписать в матричной форме, как
  
  $ \delta^L =\nabla_a C \odot \sigma'(z^L) \tag{BP1a} $
  
  Здесь ?a C определяется, как вектор,
  чьими компонентами будут частные производные ?C / ?aLj.
  Его можно представлять, как выражение скорости изменения C
  по отношению к выходным активациям.
  Легко видеть, что уравнения (BP1a) и (BP1) эквивалентны,
  поэтому далее мы будем использовать (BP1) для отсылки к любому из них.
  К примеру, в случае с квадратичной стоимостью,
  у нас будет ?a C = (aL - y),
  поэтому полной матричной формой (BP1) будет
  
  $ \delta^L = (a^L - y) \odot \sigma'(z^L) \tag{30} $
  
  Всё в этом выражении имеет удобную векторную форму,
  и его легко вычислить при помощи такой библиотеки, как, например, Numpy.
  
  Выражение ошибки ?l через ошибку в следующем слое, ?l+1: в частности,
  
  $ \delta^l = ((w^{l+1})^T \delta^{l+1}) \cdot \sigma'(z^l) \tag{BP2} $
  
  где (wl+1)T - транспонирование весовой матрицы wl+1 для слоя ?(l+1).
  
  Уравнение кажется сложным,
  но каждый его элемент легко интерпретировать.
  Допустим, мы знаем ошибку ?l+1 для слоя (l+1).
  Транспонирование весовой матрицы, (wl+1)T,
  можно представить себе, как перемещение ошибки назад по сети,
  что даёт нам некую меру ошибки на выходе слоя ?l.
  Затем мы считаем произведение Адамара ??'(zl).
  Это продвигает ошибку назад через функцию активации в слое l,
  давая нам значение ошибки ?l во взвешенном входе для слоя l.
  
  Комбинируя (BP2) с (BP1),
  мы можем подсчитать ошибку ?l для любого слоя сети.
  Мы начинаем с использования (BP1) для подсчёта ?L,
  затем применяем уравнение (BP2) для подсчёта ?L-1,
  затем снова для подсчёта ?L-2,
  и так далее, до упора по сети в обратную сторону.
  //// А вот интересный вопрос: пользуясь этими формулами,
  //// можно дойти действительно до "полного упора", т.е. входного слоя,
  //// и тогда, что будет означать "ошибка на входном слое"?
  //// Как-то не приходилось встречать попыток не только использования
  //// или интерпретации такой "ошибки на входе",
  //// а даже простого упоминания такой сущности.
  //// А ведь она что-то все-таки означает и как-то может быть использована.
  
  Уравнение скорости изменения стоимости по отношению к любому смещению в сети:
  в частности:
  
  $ \frac{\partial C}{\partial b^l_j} = \delta^l_j \tag{BP3} $
  
  То есть, ошибка ?lj точно равна скорости изменения ?C / ?blj.
  Это превосходно, поскольку (BP1) и (BP2) уже рассказали нам,
  как подсчитывать ?lj.
  Мы можем перезаписать (BP3) короче, как
  
  $ \frac{\partial C}{\partial b} = \delta \tag{31} $
  
  где ? оценивается для того же нейрона, что и смещение b.
  
  Уравнение для скорости изменения стоимости по отношению к любому весу в сети:
  в частности:
  
  $ \frac{\partial C}{\partial w^l_{jk}} = a^{l-1}_k \delta^l_j \tag{BP4} $
  
  Отсюда мы узнаём, как подсчитать частную производную ?C/?wljk
  через значения ?l и al-1,
  способ расчёта которых нам уже известен.
  Это уравнение можно переписать в менее загруженной индексами форме:
  
  $ \frac{\partial C}{\partial w} = a_{\rm in} \delta_{\rm out} \tag{32} $
  
  где ain - активация нейронного входа для веса w,
  а ?out - ошибка нейронного выхода от веса w.
  Если подробнее посмотреть на вес w и два соединённых им нейрона,
  то можно будет нарисовать это так:
  //// Честно признаюсь, что в этом месте данного текста "подзавис",
  //// как минимум, на полчаса.
  //// Совершенно контринтуитивной показалась эта формула и иллюстрация.
  //// Интуитивно представлялось, что в этой формуле должно как-то присутствовать
  //// собственно вес этой связи. А его нет.
  //// И только после долгих раздумий пришло понимание,
  //// что в формуле отражается ЗНАЧЕНИЕ ИЗМЕНЕНИЯ веса,
  //// уже как-то отнормированное к собственно значению веса.
  
  //// К сожалению автор счел, что эта формула доступна для самостоятельного вывода,
  //// и не стал ее раскрывать, оставив для самостоятельного упражнения.
  //// Честно признаюсь, с этим упражнением я не справился.
  //// И поверил в правильность записи формулы автором BP4.
  //// Но "узелок на память" завязал, на предмет поиска в других источниках.
  
  //// Особое сожаление в этом случае у меня вызывает то,
  //// что формула BP4 очень важна для понимания механизма
  //// "быстрой коррекции весов" при обратном распространении.
  //// Я ее, если и не понял, то "принял" опираясь на понимание формулы BP3.
  //// Но очень бы хотелось посмотреть на весь механизм ее вывода,
  //// именно на предмет отсутствия в ней каких-то "непроговоренных особенностей".
  
  Приятное следствие уравнения (32) в том,
  что когда активация ain мала, ain ? 0,
  член градиента ?C/?w тоже стремится к нулю.
  В таком случае мы говорим, что вес обучается медленно,
  то есть, не сильно меняется во время градиентного спуска.
  Иначе говоря, одним из следствий (BP4) будет то,
  что весовой выход нейронов с низкой активацией обучается медленно.
  
  Из (BP1)-(BP4) можно почерпнуть и другие идеи.
  Начнём с выходного слоя.
  Рассмотрим член ?'(zLj) в (BP1).
  Вспомним из графика сигмоиды из прошлой главы,
  что она становится плоской, когда ?(zLj) приближается к 0 или 1.
  В данных случаях ?'(zLj) ? 0.
  Поэтому вес в последнем слое будет обучаться медленно,
  если активация выходного нейрона мала (? 0) или велика (? 1).
  В таком случае обычно говорят,
  что выходной нейрон насыщен,
  и в итоге вес перестал обучаться
  (или обучается медленно).
  Те же замечания справедливы и для смещений выходного нейрона.
  
  Сходные идеи можно получить и касательно более ранних слоёв.
  В частности, рассмотрим член ?'(zl) в (BP2).
  Это значит, что ?lj, скорее всего, будет малой
  при приближении нейрона к насыщению.
  А это, в свою очередь, означает,
  что любые веса на входе насыщенного нейрона будут обучаться медленно
  (правда, это не сработает, если у wl+1T?l+1 будут достаточно большие элементы,
  компенсирующие небольшое значение ?'(zLj)).
  
  Подытожим:
  мы узнали, что вес будет обучаться медленно,
  если либо активация входного нейрона мала,
  либо выходной нейрон насыщен,
  то есть его активация мала или велика.
  
  В этом нет ничего особенно удивительного.
  И всё же, эти наблюдения помогают улучшить наше представление о том,
  что происходит при обучении сети.
  Более того, мы можем подойти к этим рассуждениям с обратной стороны.
  Четыре фундаментальных уравнения справедливы для любой функции активации,
  а не только для стандартной сигмоиды
  (поскольку, как мы увидим далее,
  в доказательствах не используются свойства сигмоиды).
  Поэтому эти уравнения можно использовать для разработки функций активации
  с определёнными нужными свойствами обучения.
  Для примера, допустим, мы выбрали функцию активации ?,
  непохожую на сигмоиду,
  такую, что ?' всегда положительна и не приближается к нулю.
  Это предотвратить замедление обучения,
  происходящее при насыщении обычных сигмоидных нейронов.
  Позднее в книге мы увидим примеры,
  где функция активации меняется подобным образом.
  Учитывая уравнения (BP1)-(BP4),
  мы можем объяснить, зачем нужны такие модификации,
  и как они могут повлиять на ситуацию.
  
  ......
  
  Итог: уравнения обратного распространения
  
  Задачи
  
  Альтернативная запись уравнений обратного распространения.
  Я записал уравнения обратного распространения
  с использованием произведения Адамара.
  Это может сбить с толку людей, не привыкших к этому произведению.
  Есть и другой подход, на основе обычного перемножения матриц,
  который может оказаться поучительным для некоторых читателей.
  Покажите, что (BP1) можно переписать, как
  
  $ \delta^L = \Sigma'(z^L) \nabla_a C \tag{33} $
  
  где ?'(zL) - квадратная матрица,
  у которой по диагонали расположены значения ?'(zLj),
  а другие элементы равны 0.
  Учтите, что эта матрица взаимодействует с ?a C
  через обычное перемножение матриц.
  
  Покажите, что (BP2) можно переписать, как
  
  $ \delta^l = \Sigma'(z^l) (w^{l+1})^T \delta^{l+1} \tag{34} $
  
  Комбинируя предыдущие задачи, покажите, что:
  
  $ \delta^l = \Sigma'(z^l) (w^{l+1})^T \ldots \Sigma'(z^{L-1}) (w^L)^T \Sigma'(z^L) \nabla_a C \tag{35} $
  
  Для читателей, привычных к матричному перемножению,
  это уравнение будет легче понять, чем (BP1) и (BP2).
  Я концентрируюсь на (BP1) и (BP2)
  потому, что этот подход оказывается быстрее реализовать численно.
  [здесь ? - это не сумма (?), а заглавная ? (сигма) / прим. перев.]
  
  Доказательство четырёх фундаментальных уравнений (необязательный раздел)
  
  Теперь докажем четыре фундаментальных уравнения (BP1)-(BP4).
  Все они являются следствиями цепного правила
  (правила дифференцирования сложной функции)
  из анализа функций многих переменных.
  Если вы хорошо знакомы с цепным правилом,
  настоятельно рекомендую попробовать посчитать производные самостоятельно
  перед тем, как продолжить чтение.
  
  Начнём с уравнения (BP1), которое даёт нам выражение для выходной ошибки ?L.
  Чтобы доказать его, вспомним, что, по определению:
  
  $ \delta^L_j = \frac{\partial C}{\partial z^L_j} \tag{36} $
  
  Применяя цепное правило,
  перепишем частные производные через частные производные по выходным активациям:
  
  $ \delta^L_j = \sum_k \frac{\partial C}{\partial a^L_k} \frac{\partial a^L_k}{\partial z^L_j} \tag{37} $
  
  где суммирование идёт по всем нейронам k в выходном слое.
  Конечно, выходная активация aLk нейрона ?k зависит
  только от взвешенного входа zLj для нейрона ?j, когда k=j.
  Поэтому ?aLk / ?zLj исчезает, когда k ? j.
  В итоге мы упрощаем предыдущее уравнение до
  
  $ \delta^L_j = \frac{\partial C}{\partial a^L_j} \frac{\partial a^L_j}{\partial z^L_j} \tag{38} $
  
  Вспомнив, что aLj = ?(zLj), мы можем переписать второй член справа,
  как ?'(zLj),
  и уравнение превращается в
  
  $ \delta^L_j = \frac{\partial C}{\partial a^L_j} \sigma'(z^L_j) \tag{39} $
  
  то есть, в (BP1) в покомпонентном виде.
  
  Затем докажем (BP2),
  дающее уравнение для ошибки ?l через ошибку в следующем слое ?l+1.
  Для этого нам надо переписать ?lj = ?C / ?zlj
  через ?l+1k = ?C / ?zl+1k.
  Это можно сделать при помощи цепного правила:
  
  $ \delta^l_j = \frac{\partial C}{\partial z^l_j} \tag{40} $
  
  $ = \sum_k \frac{\partial C}{\partial z^{l+1}_k} \frac{\partial z^{l+1}_k}{\partial z^l_j} \tag{41} $
  
  $ = \sum_k \frac{\partial z^{l+1}_k}{\partial z^l_j} \delta^{l+1}_k \tag{42} $
  
  где в последней строчке мы поменяли местами два члена справа,
  и подставили определение ?l+1k.
  Чтобы вычислить первый член на последней строчке, отметим, что
  
  $ z^{l+1}_k = \sum_j w^{l+1}_{kj} a^l_j +b^{l+1}_k = \sum_j w^{l+1}_{kj} \sigma(z^l_j) +b^{l+1}_k \tag{43} $
  
  Продифференцировав, получим
  
  $ \frac{\partial z^{l+1}_k}{\partial z^l_j} = w^{l+1}_{kj} \sigma'(z^l_j). \tag{44} $
  
  Подставив это в (42), получим
  
  $ \delta^l_j = \sum_k w^{l+1}_{kj} \delta^{l+1}_k \sigma'(z^l_j). \tag{45} $
  
  То есть, (BP2) в покомпонентной записи.
  
  Остаётся доказать (BP3) и (BP4).
  Они тоже следуют из цепного правила,
  примерно таким же методом, как и два предыдущих.
  Оставлю их вам в качестве упражнения.
  
  Упражнение
  
  Докажите (BP3) и (BP4).
  
  //// Как я уже отметил выше, с этим упражнением я не справился.
  
  Вот и всё доказательство
  четырёх фундаментальных уравнений обратного распространения.
  Оно может показаться сложным.
  Но на самом деле это просто результат аккуратного применения цепного правила.
  Говоря менее лаконично, обратное распространение можно представить себе,
  как способ подсчёта градиента функции стоимости
  через систематическое применение цепного правила
  из анализа функций многих переменных.
  И это реально всё, что представляет собой обратное распространение
  - остальное просто детали.
  
  Алгоритм обратного распространения
  
  Уравнения обратного распространения дают нам
  метод подсчёта градиента функции стоимости.
  Давайте запишем это явно в виде алгоритма:
  
  Вход x:
  назначить соответствующую активацию a1 для входного слоя.
  
  Прямое распространение:
  для каждого l = 2,3,:,L вычислить zl = wlal?1+bl и al = ?(zl).
  
  Выходная ошибка ?L:
  вычислить вектор ?L = ?a C ? ?'(zL).
  
  Обратное распространение ошибки:
  для каждого l = L?1,L?2,:,2 вычислить ?l = ((wl+1)T?l+1) ? ?'(zl).
  
  Выход:
  градиент функции стоимости задаётся
  $\frac{\partial C}{\partial w^l_{jk}} = a^{l-1}_k \delta^l_j$ и $\frac{\partial C}{\partial b^l_j} = \delta^l_j$.
  
  Посмотрев на алгоритм, вы поймёте,
  почему он называется обратное распространение.
  Мы вычисляем векторы ошибки ?l задом наперёд,
  начиная с последнего слоя.
  Может показаться странным, что мы идём по сети назад.
  Но если подумать о доказательстве обратного распространения,
  то обратное движение является следствием того,
  что стоимость - это функция выхода сети.
  Чтобы понять, как меняется стоимость в зависимости от ранних весов и смещений,
  нам нужно раз за разом применять цепное правило,
  идя назад через слои,
  чтобы получить полезные выражения.
  
  Упражнения
  
  Обратное распространение с одним изменённым нейроном.
  Допустим, мы изменили один нейрон в сети с прямым распространением так,
  чтобы его выход был f(?j wjxj+b),
  где f - некая функция, не похожая на сигмоиду.
  Как нам поменять алгоритм обратного распространения в данном случае?
  
  Обратное распространение с линейными нейронами.
  Допустим, мы заменим обычную нелинейную сигмоиду на ?(z) = z по всей сети.
  Перепишите алгоритм обратного распространения для данного случая.
  
  Как я уже пояснял ранее, алгоритм обратного распространения
  вычисляет градиент функции стоимости для одного обучающего примера, C = Cx.
  На практике часто комбинируют обратное распространение с алгоритмом обучения,
  например, со стохастическим градиентным спуском,
  когда мы подсчитываем градиент для многих обучающих примеров.
  В частности, при заданном мини-пакете m обучающих примеров,
  следующий алгоритм применяет градиентный спуск на основе этого мини-пакета:
  
  Вход: набор обучающих примеров.
  
  Для каждого обучающего примера x назначить
  соответствующую входную активацию ax,1 и выполнить следующие шаги:
  
  Прямое распространение для каждого l=2,3,..,L
  вычислить zx,l = wlax,l?1+bl и ax, l = ?(zx,l).
  
  Выходная ошибка ?x,L:
  вычислить вектор ?x,L = ?a Cx ? ?'(zx,L).
  
  Обратное распространение ошибки:
  для каждого l=L?1,L?2,:,2 вычислить ?x,l = ((wl+1)T?x,l+1) ? ?'(zx,l).
  
  Градиентный спуск:
  для каждого l=L,L?1,:,2
  обновить веса согласно правилу
  $w^l \rightarrow w^l-\frac{\eta}{m} \sum_x \delta^{x,l} (a^{x,l-1})^T$,
  и смещения согласно правилу
  $b^l \rightarrow b^l-\frac{\eta}{m} \sum_x \delta^{x,l}$.
  
  Конечно, для реализации стохастического градиентного спуска
  на практике также понадобится внешний цикл,
  генерирующий мини-пакеты обучающих примеров,
  и внешний цикл,
  проходящий по нескольким эпохам обучения.
  Для простоты я их опустил.
  
  Код для обратного распространения
  
  Поняв абстрактную сторону обратного распространения,
  теперь мы можем понять код,
  использованный в предыдущей главе,
  реализующий обратное распространение.
  
  ........
  
  Большую часть работы делают строки
  delta_nabla_b, delta_nabla_w = self.backprop(x, y),
  использующие метод backprop для подсчёта частных производных
  ?Cx/?blj и ?Cx/?wljk.
  Метод backprop почти повторяет алгоритм предыдущего раздела.
  Есть одно небольшое отличие
  - мы используем немного другой подход к индексированию слоёв.
  Это сделано для того, чтобы воспользоваться особенностью python,
  отрицательными индексами массива,
  позволяющими отсчитывать элементы назад, с конца.
  l[-3] будет третьим элементом с конца массива l.
  Код backprop приведён ниже,
  вместе со вспомогательными функциями,
  используемыми для подсчёта сигмоиды,
  её производной и производной функции стоимости.
  С ними код получается законченным и понятным.
  Если что-то неясно, обратитесь к первому описанию кода с полным листингом.
  
  ........
  
  В каком смысле обратное распространение является быстрым алгоритмом?
  
  В каком смысле обратное распространение является быстрым алгоритмом?
  Для ответа на этот вопрос рассмотрим ещё один подход к вычислению градиента.
  Представьте себе ранние дни исследований нейросетей.
  Возможно, это 1950-е или 1960-е годы,
  и вы - первый человек в мире,
  придумавший использовать для обучения градиентный спуск!
  Но чтобы это сработало, вам нужно подсчитать градиент функции стоимости.
  Вы вспоминаете алгебру и решаете посмотреть,
  можно ли использовать цепное правило для вычисления градиента.
  Немного поигравшись, вы видите, что алгебра кажется сложной,
  и вы разочаровываетесь.
  Вы пытаетесь найти другой подход.
  Вы решаете считать стоимость функцией только весов C=C(w)
  (к смещениям вернёмся чуть позже).
  Вы нумеруете веса w1, w2,...
  и хотите вычислить ?C/?wj для веса wj.
  Очевидный способ - использовать приближение
  
  $ \frac{\partial C}{\partial w_{j}} \approx \frac{C(w+\epsilon e_j)-C(w)}{\epsilon} \tag{46} $
  
  Где ? > 0 - небольшое положительное число,
  а ej - единичный вектор направления j.
  Иначе говоря, мы можем приблизительно оценить ?C/?wj,
  вычислив стоимость C для двух немного различных значений wj,
  а потом применить уравнение (46).
  Та же идея позволит нам подсчитать частные производные ?C/?b по смещениям.
  
  Подход выглядит многообещающим.
  Концептуально простой, легко реализуемый,
  использует только несколько строк кода.
  Он выглядит гораздо более многообещающим,
  чем идея использования цепного правила для подсчёта градиента!
  
  К сожалении, хотя такой подход выглядит многообещающим,
  при его реализации в коде оказывается,
  что работает он крайне медленно.
  Чтобы понять, почему, представьте, что у нас в сети миллион весов.
  Тогда для каждого веса wj нам нужно вычислить C(w + ?ej),
  чтобы подсчитать ?C/?wj.
  А это значит, что для вычисления градиента нам нужно вычислить
  функцию стоимости миллион раз,
  что потребует миллион прямых проходов по сети (на каждый обучающий пример).
  А ещё нам надо подсчитать C(w),
  так что получается миллион и один проход по сети.
  
  Хитрость обратного распространения в том,
  что она позволяет нам одновременно вычислять все частные производные ?C/?wj,
  используя только один прямой проход по сети,
  за которым следует один обратный проход.
  Грубо говоря, вычислительная стоимость обратного прохода примерно такая же,
  как у прямого.
  
  Поэтому общая стоимость обратного распространения примерно такая же,
  как у двух прямых проходов по сети.
  Сравните это с миллионом и одним прямым проходом,
  необходимым для реализации метода (46)!
  Так что, хотя обратное распространение внешне выглядит более сложным подходом,
  в реальности он куда как более быстрый.
  
  Впервые это ускорение сполна оценили в 1986,
  и это резко расширило диапазон задач, решаемых с помощью нейросетей.
  В свою очередь, это привело к увеличению количества людей,
  использующих нейросети.
  Конечно, обратное распространение - не панацея.
  Даже в конце 1980-х люди уже натолкнулись на её ограничения,
  особенно при попытках использовать обратное распространение
  для обучения глубоких нейросетей,
  то есть сетей со множеством скрытых слоёв.
  Позже мы увидим, как современные компьютеры
  и новые хитрые идеи позволяют использовать обратное распространение
  для обучения таких глубоких нейросетей.
  
  Обратное распространение: в общем и целом
  
  Как я уже объяснил, обратное распространение являет нам две загадки.
  Первая, что на самом деле делает алгоритм?
  Мы выработали схему обратного распространения ошибки от выходных данных.
  Можно ли углубиться дальше,
  получить более интуитивное представление о происходящем
  во время всех этих перемножений векторов и матриц?
  Вторая загадка - как вообще кто-то мог обнаружить обратное распространение?
  Одно дело, следовать шагам алгоритма или доказательству его работы.
  Но это не значит, что вы так хорошо поняли задачу,
  что могли изобрести этот алгоритм.
  Есть ли разумная цепочка рассуждений, способная привести нас
  к открытию алгоритма обратного распространения?
  В этом разделе я освещу обе загадки.
  
  Чтобы улучшить понимание работы алгоритма,
  представим, что мы провели небольшое изменение ?wljk некоего веса wljk:
  
  Это изменение веса приведёт к изменению выходной активации
  соответствующего нейрона:
  
  Это приведёт к изменению всех активаций следующего слоя:
  
  Эти изменения приведут к изменениям следующего слоя, и так далее,
  вплоть до последнего,
  а потом к изменениям функции стоимости:
  
  Изменение ?C связано с изменением ?wljk уравнением
  
  $ \Delta C \approx \frac{\partial C}{\partial w^l_{jk}} \Delta w^l_{jk} \tag{47} $
  
  Из этого следует, что вероятным подходом к вычислению ?C/?wljk
  будет тщательное отслеживание распространения небольшого изменения wljk,
  приводящего к небольшому изменению в C.
  Если мы сможем это сделать,
  тщательно выражая по пути всё в величинах,
  которые легко вычислить, то мы сможем вычислить и ?C/?wljk.
  
  Давайте попробуем.
  Изменение ?wljk вызывает небольшое изменение ?alj
  в активации нейрона j в слое l.
  Это изменение задаётся
  
  $ \Delta a^l_j \approx \frac{\partial a^l_j}{\partial w^l_{jk}} \Delta w^l_{jk} \tag{48} $
  
  Изменение активации ?alj приводит к изменениям
  во всех активациях следующего слоя, (l+1).
  Мы сконцентрируемся только на одной из этих изменённых активаций,
  допустим, al+1q,
  
  Это приведёт к следующим изменениям:
  
  $ \Delta a^{l+1}_q \approx \frac{\partial a^{l+1}_q}{\partial a^l_j} \Delta a^l_j \tag{49} $
  
  Подставляя уравнение (48), получаем:
  
  $ \Delta a^{l+1}_q \approx \frac{\partial a^{l+1}_q}{\partial a^l_j} \frac{\partial a^l_j}{\partial w^l_{jk}} \Delta w^l_{jk} \tag{50} $
  
  Конечно, изменение ?al+1q изменит и активации в следующем слое.
  Мы даже можем представить путь по всей сети от wljk до C,
  где каждое изменение активации приводит к изменению следующей активации,
  и, наконец, к изменению стоимости на выходе.
  Если путь проходит через активации alj, al+1q,:,aL?1n, aLm,
  тогда итоговое выражение будет
  
  $ \Delta C \approx \frac{\partial C}{\partial a^L_m} \frac{\partial a^L_m}{\partial a^{L-1}_n} \frac{\partial a^{L-1}_n}{\partial a^{L-2}_p} \ldots \frac{\partial a^{l+1}_q}{\partial a^l_j} \frac{\partial a^l_j}{\partial w^l_{jk}} \Delta w^l_{jk} \tag{51} $
  
  То есть, мы выбираем член вида ?a/?a
  для каждого следующего проходимого нами нейрона,
  а также для члена ?C / ?aLm в конце.
  Это представление изменений в C из-за изменений в активациях
  по данному конкретному пути сквозь сеть.
  Конечно, существует много путей, по которым изменение в wljk
  может пройти и повлиять на стоимость,
  а мы рассматривали только один из них.
  Чтобы подсчитать общее изменение C разумно предположить,
  что мы должны просуммировать все возможные пути от веса до конечной стоимости:
  
  $ \Delta C \approx \sum_{mnp\ldots q} \frac{\partial C}{\partial a^L_m} \frac{\partial a^L_m}{\partial a^{L-1}_n} \frac{\partial a^{L-1}_n}{\partial a^{L-2}_p} \ldots \frac{\partial a^{l+1}_q}{\partial a^l_j} \frac{\partial a^l_j}{\partial w^l_{jk}} \Delta w^l_{jk} \tag{52} $
  
  где мы просуммировали все возможные выборы для промежуточных нейронов по пути.
  Сравнивая это с (47), мы видим, что:
  
  $ \frac{\partial C}{\partial w^l_{jk}} = \sum_{mnp\ldots q} \frac{\partial C}{\partial a^L_m} \frac{\partial a^L_m}{\partial a^{L-1}_n} \frac{\partial a^{L-1}_n}{\partial a^{L-2}_p} \ldots \frac{\partial a^{l+1}_q}{\partial a^l_j} \frac{\partial a^l_j}{\partial w^l_{jk}}. \tag{53} $
  
  Уравнение (53) выглядит сложно.
  Однако у него есть приятная интуитивная интерпретация.
  Мы подсчитываем изменение C по отношению к весам сети.
  Оно говорит нам, что каждое ребро между двумя нейронами сети
  связано с фактором отношения,
  являющимся только лишь частной производной активации одного нейрона
  по отношению к активации другого нейрона.
  У ребра от первого веса до первого нейрона фактор отношения равен ?alj / ?wljk.
  Коэффициент отношения для пути
  - это просто произведение коэффициентов по всему пути.
  А общий коэффициент изменения ?C / ?wljk
  является суммой коэффициентов по всем путям
  от начального веса до конечной стоимости.
  Эта процедура показана далее, для одного пути:
  
  Пока что мы давали эвристический аргумент,
  способ представить происходящее при изменении весов сети.
  Позвольте мне обрисовать дальнейший вариант мышления на эту тему
  для развития данного аргумента.
  Во-первых, можно вывести точные выражение
  для всех отдельных частных производных в уравнении (53).
  Это легко сделать с использованием несложной алгебры.
  После этого можно попробовать понять,
  как записать все суммы по индексам в виде произведений матриц.
  Это оказывается утомительной задачей,
  требующей терпения,
  но не чем-то экстраординарным.
  После всего этого и максимального упрощения
  вы увидите, что получился ровно тот же самый
  алгоритм обратного распространения!
  Поэтому алгоритм обратного распространения можно представлять себе,
  как способ вычисления суммы коэффициентов по всем путям.
  Или, если переформулировать,
  алгоритм обратного распространения
  - хитроумный способ отслеживания небольших изменений весов (и смещений),
  когда они распространяются по сети,
  достигают выхода и влияют на стоимость.
  
  Здесь я не буду делать всего этого.
  Это дело малопривлекательное, требующее тщательной проработки деталей.
  Если вы готовы к такому, вам может понравиться этим заниматься.
  Если нет, то надеюсь, что подобные размышления дадут вам
  некоторые идеи по поводу целей обратного распространения.
  
  Что насчёт другой загадки
  - как вообще можно было открыть обратное распространение?
  На самом деле, если вы последуете по обрисованному мною пути,
  вы получите доказательство обратного распространения.
  К несчастью, доказательство будет длиннее и сложнее
  чем то, что я описал ранее.
  Так как же было открыто то короткое
  (но ещё более загадочное) доказательство?
  Если записать все детали длинного доказательства,
  вам сразу бросятся в глаза несколько очевидных упрощений.
  Вы применяете упрощения,
  получаете более простое доказательство,
  записываете его.
  А затем вам опять на глаза попадаются несколько очевидных упрощений.
  И вы повторяете процесс.
  После нескольких повторений получится то доказательство,
  что мы видели ранее - короткое, но немного непонятное,
  поскольку из него удалены все путеводные вехи!
  Я, конечно, предлагаю вам поверить мне на слово,
  однако никакой загадки происхождения приведённого доказательства
  на самом деле нет.
  Просто много тяжёлой работы по упрощению доказательства,
  описанного мною в этом разделе.
  
  Однако в этом процессе есть один хитроумный трюк.
  В уравнении (53) промежуточные переменные
  - это активации, типа al+1q.
  Хитрость в том, чтобы перейти к использованию взвешенных входов,
  типа zl+1q, в качестве промежуточных переменных.
  Если не пользоваться этим,
  и продолжать использовать активации,
  полученное доказательство будет немногим более сложным,
  чем данное ранее в этой главе.
  //// Так я и не понял: эти "взвешенные ходы" реальные,
  //// т.е. как-то соответствующие каким-то "ячейкам памяти"
  //// или чисто "виртуальные", т.е. используемые только в формальной записи.
  //// А сам по себе прием известен в ТРИЗ
  //// вроде как, под названием "использовать промежуточный результат".
  //// Стоит запомнить это его применение.
  
  .......
  
  //// Конец цитирования.
  
  Очень трудный был материал, несмотря на то,
  что он как раз наиболее понятный из множества аналогичных по этой теме.
  И труден он даже не потому, что много математики,
  как раз кое в чем этой "чистой математики" мне чуть-чуть не хватило.
  Сложен в том, что упомянуто много деталей "обратного распространения",
  обычно не удостаиваемых внимания,
  но которые нужно как-то уложить в достаточно полную и непротиворечивую систему.
  
  Что-то в голове явно оформилось, но не скажу, что до конца,
  и есть над чем работать при проработке следующих глав,
  такого замечательного учебника.
  Так что, впереди следующие "погружения".
  
  
  ========
  
  26.03.2024 21:47
  
  "Звонок другу".
  В оглавление.
  
  Все-таки надо стараться не накапливать "технический долг"
  даже в процессе обучения "машинному обучению".
  Вот я и решил закрыть парочку, оставшихся у меня "непоняток"
  при проработке предыдущих материалов, обратившись за помощью к Copilot.
  
  24.03.2024 19:28
  
  .....
  
  //// Спасибо, Copilot. Общее представление о том, как должна выводится
  //// эта формула я имею.
  //// Но конкретно в тексте данной статьи эта формула какая-то совсем короткая
  //// без всяких производных и даже без собственно значения вес и функций активации.
  //// Вот этот текст:
  //// "Уравнение для скорости изменения стоимости по отношению к любому весу в сети:
  //// в частности:
  ////
  //// Отсюда мы узнаём, как подсчитать частную производную ?C/?wljk
  //// через значения ?l и al-1, способ расчёта которых нам уже известен.
  //// Это уравнение можно переписать в менее загруженной индексами форме:
  ////
  //// где ain - активация нейронного входа для веса w,
  //// а ?out - ошибка нейронного выхода от веса w.
  //// Если подробнее посмотреть на вес w и два соединённых им нейрона,
  //// то можно будет нарисовать это так:"
  ////
  //// Т.е. значение производной изменения веса равна
  //// произведению активации предыдущего слоя на ошибку следующего слоя.
  //// И вроде как интуитивно я понимаю, что все, наверное, верно
  //// но как это получается понять не могу.
  
  > Copilot 19:52
  
  > Давайте попробуем разобраться в этом вопросе.
  > Формула, о которой идет речь, отражает
  > один из ключевых моментов алгоритма обратного распространения ошибки
  >- как изменение веса влияет на общую ошибку сети.
  > В контексте нейронных сетей,
  > это изменение часто выражается через градиент функции потерь
  > по отношению к весам.
  
  > Формула, которую вы упомянули, говорит о том,
  > что градиент функции потерь ( \frac{\partial C}{\partial w_{ljk}} )
  > по отношению к весу ( w_{ljk} ) на ( l )-м слое
  > можно вычислить как произведение активации ( a_{in} ) предыдущего слоя
  > на ошибку ( \delta_{out} ) следующего слоя.
  > В математической форме это выглядит так:
  
  > $$\frac{\partial C}{\partial w_{ljk}} = a_{in} \cdot \delta_{out}$$Здесь:
  
  >  ( a_{in} ) - это активация нейрона на предыдущем слое,
  > который связан с весом ( w_{ljk} ).
  >  ( \delta_{out} ) - это градиент ошибки по активации нейрона на текущем слое,
  > который также связан с весом ( w_{ljk} ).
  
  > Это уравнение показывает,
  > что для обновления веса ( w_{ljk} ),
  > нам нужно знать, насколько сильно активация предыдущего нейрона
  > влияет на ошибку,
  > и насколько сильно сам вес влияет на активацию текущего нейрона.
  
  > Почему это работает?
  > Потому что в процессе обучения мы хотим минимизировать функцию потерь,
  > и для этого нам нужно знать направление,
  > в котором нужно изменить веса,
  > чтобы ошибка уменьшилась.
  > Градиент функции потерь по весам дает нам это направление.
  
  > Если говорить более наглядно, представьте,
  > что у вас есть крутящийся вентилятор,
  > и вы хотите замедлить его вращение.
  > Вы знаете, что если нажать на одно из лопастей в определенном направлении,
  > вентилятор замедлится.
  > В контексте нейронной сети, ( a_{in} ) - это ваша рука,
  > ( \delta_{out} ) - это сила и направление нажатия,
  > а ( w_{ljk} ) - это лопасть вентилятора.
  > Вычисляя ( a_{in} \cdot \delta_{out} ),
  > вы определяете, как именно нужно нажать на лопасть,
  > чтобы добиться желаемого эффекта.
  
  > Надеюсь, это объяснение помогло вам лучше понять,
  > как работает формула и почему она так важна
  > для процесса обучения нейронных сетей.
  
  ......
  
  /// Спасибо, Copilot. Вроде все понимаю,
  /// но почему-то интуитивно представлялось,
  /// что в этой формуле в какой-то форме будет присутствовать
  /// собственно значение веса w_{ljk},
  /// А его нет.
  /// И я не могу понять, где он "сокращается" или, наоборот, учтен.
  /// Собственно вопрос именно в этом:
  /// где спряталось "значение w_{ljk}"
  /// или почему я его и не должен видеть?
  /// Так, может быть, более понятно суть моего недопонимания этой формулы.
  
  > Copilot 20:05
  
  > Ваш вопрос очень важен для понимания механизма обучения нейронных сетей.
  > Давайте рассмотрим, почему в формуле градиента ошибки по весу ( w_{ljk} )
  > не присутствует сам вес.
  
  > В контексте обратного распространения ошибки,
  > когда мы вычисляем градиент функции потерь по весу ( w_{ljk} ),
  > мы фактически спрашиваем:
  > "Как изменение этого веса повлияет на общую ошибку сети?".
  > Мы не интересуемся текущим значением веса,
  > а хотим знать, как его небольшое изменение изменит ошибку.
  
  > Формула, которую вы упомянули:
  > $$\frac{\partial C}{\partial w_{ljk}} = a_{in} \cdot \delta_{out}$$
  > говорит о том, что градиент ошибки по весу
  > равен произведению активации предыдущего нейрона (( a_{in} ))
  > и градиента ошибки по активации следующего нейрона (( \delta_{out} )).
  > Здесь ( w_{ljk} ) не появляется напрямую,
  > потому что мы рассматриваем его влияние
  > через изменение активации следующего нейрона.
  
  > Вес ( w_{ljk} ) "учтен" в том смысле,
  > что он влияет на активацию следующего нейрона,
  > и это влияние мы уже учитываем,
  > когда вычисляем ( \delta_{out} ).
  > То есть, когда мы вычисляем градиент ошибки по активации следующего нейрона,
  > мы уже учитываем, как изменение веса повлияет на эту активацию,
  > и, следовательно, на ошибку.
  
  > Если бы мы рассматривали производную активации по весу,
  > тогда в формуле присутствовал бы сам вес:
  
  > $$\frac{\partial a_{out}}{\partial w_{ljk}} = a_{in}$$
  
  > Здесь мы видим, что изменение активации выходного нейрона (( a_{out} ))
  > по отношению к весу (( w_{ljk} ))
  > пропорционально активации входного нейрона (( a_{in} )),
  > и в этом случае значение веса не учитывается,
  > потому что мы рассматриваем линейную зависимость
  > между весом и активацией.
  
  > Надеюсь, это объяснение помогло вам лучше понять,
  > почему в формуле градиента ошибки по весу
  > не фигурирует сам вес.
  
  ......
  
  //// Большое спасибо, Copilot.
  //// Надеюсь, я понял и теперь долго сохраню это понимание.
  
  
  25.03.2024 21:23
  
  ......
  
  //// Вопрос очень конкретный и, наверно, простой и короткий.
  //// В тексте, в котором описывается вывод формул обратного распространения ошибки
  //// Встретился такой абзац:
  //// "Используя уравнение (25) для вычисления al,
  //// мы вычисляем промежуточное значение zl ? wlal?1+bl.
  //// Эта величина оказывается достаточно полезной для именования:
  //// мы называем zl взвешенным входом нейронов слоя ?l.
  //// Позднее мы активно будем использовать этот взвешенный вход."
  
  //// Вопрос в том является ли это "промежуточное значение",
  //// так сказать, "виртуальным",
  //// Т.е. используемым только для вывода формул,
  //// Или все-таки "реальным",
  //// в том смысле, что при прямом проходе
  //// эти значения запоминаются в каких-то ячейках памяти,
  //// и затем используются при обратном распространении.
  //// Понятно, что это зависит от конкретной реализации алгоритма,
  //// Но стало интересно:
  //// как это реализуется в широко распространенных библиотеках машинного обучения?
  
  > Copilot 21:33
  
  > Величина ( z_l ), которую вы упомянули,
  > и называемую взвешенным входом,
  > является действительно "реальной" в контексте обучения нейросетей.
  > В процессе прямого распространения (forward pass),
  > когда данные проходят через сеть,
  > для каждого слоя вычисляются активации нейронов.
  > Эти активации вычисляются как функция от взвешенного входа,
  > который в свою очередь является линейной комбинацией
  > входных данных предыдущего слоя и весов текущего слоя, плюс смещение (bias).
  
  > Взвешенный вход ( z_l ) используется
  > не только для вывода формул в теоретических расчетах,
  > но и сохраняется в памяти во время прямого прохода.
  > Это делается для того, чтобы во время обратного распространения ошибки
  > (backward pass)
  > можно было эффективно вычислить градиенты функции потерь по весам и смещениям.
  > Значения ( z_l ) необходимы для вычисления градиентов по активациям и весам,
  > так как они входят в производные функций активации,
  > которые используются при обновлении весов.
  
  > В широко распространенных библиотеках машинного обучения,
  > таких как TensorFlow и PyTorch,
  > этот процесс автоматизирован.
  > Когда вы строите модель и запускаете обучение,
  > библиотека автоматически сохраняет необходимые значения
  > во время прямого прохода и использует их во время обратного прохода
  > для вычисления градиентов.
  > Это одна из причин, почему обучение глубоких нейронных сетей
  > требует значительного объема оперативной памяти,
  > так как необходимо хранить все промежуточные значения
  > для каждого элемента обучающего набора данных.
  > Это также объясняет, почему глубокое обучение
  > часто использует графические процессоры (GPU)
  > или специализированные процессоры для машинного обучения (TPU),
  > которые имеют большое количество быстрой памяти,
  > оптимизированной для таких операций.
  
  //// Спасибо за исчерпывающий ответ, Copilot. Все понятно.
  //// Только хотелось бы задать еще один связанный с ним вопрос, но уже по ТРИЗ.
  //// Какому приему ТРИЗ соответствует такое сохранение промежуточных значений,
  //// И последующее их повторное использование?
  
  > Copilot 21:42
  
  > Сохранение промежуточных значений и их последующее использование
  > в контексте ТРИЗ может соответствовать нескольким приемам,
  > но один из наиболее подходящих
  > - это прием "использование внутренних ресурсов системы".
  > Этот прием подразумевает использование уже имеющихся элементов в системе
  > для достижения цели или решения задачи.
  > В случае с нейросетями, промежуточные значения,
  > полученные во время прямого прохода, являются внутренними ресурсами,
  > которые затем используются для обратного распространения ошибки
  > и обновления весов.
  > Это позволяет оптимизировать процесс обучения
  > и делает его более эффективным,
  > что соответствует принципу идеальности в ТРИЗ
  > - достижение максимального результата с минимальными усилиями
  > https://practicum.yandex.ru/blog/chto-takoe-metod-triz/.
  
  //// Спасибо, просто здорово, Copilot.
  
  С этими непонятками, вроде, разобрались.
  А вопрос про "стохастический градиентный спуск"
  я решил задать немного позже
  Появились мысли как это можно увязать с другой темой,
  а именно, с концепцией "терраформирования ландшафта" задачи/решения.
  Так что, это пока не к спеху. Надо все это получше продумать.
  
  
  =========
  
  31.03.2024 17:31
  
  Маленькие улучшения backpropagation.
  В оглавление.
  
  Продолжаем "погружаться" в не очень хитрые мудрености backpropagation
  - метода "обратного распространения ошибки".
  Данный материал не слишком сложный, формул не много, и они не сложны,
  а самое приятно, что по факту они толком то и не нужны,
  чтобы понять смысл наиболее распространенных ухищрений
  по улучшению результатов стандартного backpropagation.
  Зато на нем можно увидеть возможность совершенно других направлений
  совершенствования "обратного распространения",
  которые "почему-то" как-то "не на слуху":
  
  "Нейросети и глубокое обучение,
  глава 3, ч.1: улучшение способа обучения нейросетей".
  Автор: SLY_G (Вячеслав Голованов)
  https://habr.com/ru/articles/458724/.
  8 июл 2019 в 10:00
  
  Автор оригинала: Michael Nielsen
  http://neuralnetworksanddeeplearning.com/chap3.html
  
  //// Начало цитирования.
  
  .......
  
  Среди техник, которым мы научимся в этой главе:
  лучший вариант на роль функции стоимости,
  а именно функция стоимости с перекрёстной энтропией;
  четыре т.н. метода регуляризации
  (регуляризации L1 и L2, исключение нейронов [dropout],
  искусственное расширение обучающих данных),
  улучшающих обобщаемость наших НС за пределы обучающих данных;
  лучший метод инициализации весов сети;
  набор эвристических методов,
  помогающих выбирать хорошие гиперпараметры для сети.
  Я также рассмотрю и несколько других техник, чуть более поверхностно.
  Эти обсуждения по большей части не зависят друг от друга,
  поэтому их можно по желанию перепрыгивать.
  Мы также реализуем множество технологий в рабочем коде
  и используем их для улучшения результатов,
  полученных для задачи классификации рукописных цифр, изученной в главе 1.
  
  Конечно, мы рассматриваем лишь малую толику огромного количества техник,
  разработанных для использования с нейросетями.
  Суть в том, что лучший способ войти в мир изобилия доступных техник
  - это подробное изучение нескольких самых важных из них.
  Овладение этими важными техниками не только полезно само по себе,
  оно ещё углубит ваше понимание того,
  какие проблемы могут возникать при использовании нейросетей.
  В итоге вы будете подготовлены для быстрой адаптации новых техник по необходимости.
  
  Функция стоимости с перекрёстной энтропией
  
  Большинству из нас неприятно быть неправым.
  Вскоре после начала обучения игре на пианино
  я дал небольшой концерт перед аудиторией.
  Я нервничал, и начал играть произведение на октаву ниже, чем надо.
  Я запутался, и не мог продолжать, пока кто-то не указал мне на ошибку.
  Мне было очень стыдно.
  Однако, хотя это и неприятно, мы также очень быстро обучаемся,
  решив, что мы ошиблись.
  И уж точно в следующий раз, когда я выступал перед аудиторией,
  я играл в нужной октаве!
  И наоборот, мы обучаемся медленнее,
  когда наши ошибки определены не очень хорошо.
  
  В идеале мы ожидаем,
  что наши нейросети будут обучаться быстро на своих ошибках.
  Происходит ли это на практике?
  Для ответа на этот вопрос посмотрим на надуманный пример.
  В нём участвует нейрон всего с одним входом:
  
  .......
  
  Мы обучаем этот нейрон делать нечто до смешного простое:
  принять на вход 1 и выдать 0.
  Конечно, решение такой тривиальной задачи мы могли бы найти,
  подобрав вес и смещение вручную,
  не используя обучающий алгоритм.
  Однако довольно полезно будет попытаться использовать градиентный спуск,
  чтобы получить в результате обучения вес и смещение.
  Давайте посмотрим на то, как обучается нейрон.
  
  Для определённости я выберу изначальный вес 0,6 и изначальное смещение 0,9.
  Это некие общие величины, назначенные в качестве отправной точки,
  и я не выбирал их специально.
  Изначально нейрон на выходе выдаёт 0,82,
  поэтому нам нужно многому научиться,
  чтобы он приблизился к желаемому выводу 0,0.
  В оригинале статьи есть интерактивная форма,
  на которой можно кликнуть и наблюдать процесс обучения.
  Это не записанная заранее анимация,
  браузер реально вычисляет градиент,
  а потом использует его для обновления веса и смещения,
  и демонстрирует результат.
  Скорость обучения ?=0,15, достаточно медленная,
  чтобы могли видеть, что происходит,
  но достаточно быстрая, чтобы обучение шло за секунды.
  Функция стоимости С - квадратичная, введённая в первой главе.
  Я вскоре напомню вам об её точной форме,
  поэтому не обязательно возвращаться и рыться там.
  Обучение можно запускать несколько раз, просто нажимая на кнопку .
  
  Как видите, нейрон быстро выучивает вес и смещение, понижающие стоимость,
  и даёт на выходе 0,09.
  Это не совсем желаемый результат 0,0, но достаточно неплохой.
  Допустим, что мы вместо этого выберем начальные вес и смещение 2,0.
  В данном случае изначальный выход будет равен 0,98, что совсем неверно.
  Давайте посмотрим, как в этом случае нейрон будет учиться выдавать 0.
  
  Хотя этот пример использует ту же скорость обучения (?=0,15),
  мы видим, что обучение проходит медленнее.
  Порядка 150 первых эпох веса и смещения почти не меняются.
  Затем обучение разгоняется,
  и, почти как в первом примере, нейрон быстро движется к 0,0.
  такое поведение странное, не похожее на обучение человека.
  Как я сказал в начале, мы часто быстрее всего учимся,
  когда сильно ошибаемся.
  Но мы только что видели, как наш искусственный нейрон с большим трудом учится,
  сильно ошибаясь - гораздо труднее,
  чем когда он ошибался немного.
  Более того, оказывается, что такое поведение возникает
  не только в нашем простом примере,
  но и в НС более общего назначения.
  //// Очень интересное замечание. Завяжу-ка "узелок на память".
  //// Нужно подумать, как с этим обстоят дело в битовых нейросетях?
  //// И что из этого следует?
  Почему обучение идёт так медленно?
  Можно ли найти способ избежать данной проблемы?
  
  Чтобы понять источник проблемы, вспомним,
  что наш нейрон обучается через изменение веса и смещения со скоростью,
  определяемой частными производными функции стоимости, ?C/?w и ?C/?b.
  Так что сказать <обучение идёт медленно>,
  это всё равно, что сказать, что эти частные производные малы.
  Проблема в том, чтобы понять, почему они малы.
  Для этого давайте вычислим частные производные.
  Вспомним, что мы используем квадратичную функцию стоимости,
  которая задаётся уравнением (6):
  
  $ C = \frac{(y-a)^2}{2} \tag{54} $
  
  где a - выход нейрона, когда на входе используется x=1,
  и y=0 - желаемый выход.
  Чтобы записать это непосредственно через вес и смещение,
  вспомним, что a=?(z), где z=wx+b.
  Используя цепное правило для дифференцирования по весу и смещению, получаем:
  
  $ \frac{\partial C}{\partial w} = (a-y)\sigma'(z) x = a \sigma'(z) \tag{55} $
  
  $ \frac{\partial C}{\partial b} = (a-y)\sigma'(z) = a \sigma'(z) \tag{56} $
  
  где я подставил x=1 и y=0.
  Чтобы понять поведение этих выражений,
  давайте подробнее взглянем на член \sigma'(z) справа. Вспомним форму сигмоиды:
  
  ........
  
  Из графика видно, что когда выход нейрона близок к 1,
  кривая становится очень плоской,
  и \sigma'(z) становится маленькой.
  Уравнения (55) и (56) говорят нам о том,
  что ?C/?w и ?C/?b становятся очень маленькими.
  Отсюда и растёт замедление обучения.
  Более того, как мы увидим чуть позже,
  замедление обучения происходит,
  по сути, по той же причине и в НС более общего характера,
  а не только в нашем простейшем примере.
  
  //// А зачем мы применяем "сигмоиду"?
  //// Чтобы ограничить диапазон значений
  //// и оставить функцию активации непрерывной,
  //// гладкой и легко вычисляемой и ее самой и ее производной.
  //// И это входит в конфликт/противоречие с требованием "скорости обучения".
  //// Типичное техническое противоречие.
  //// И как же оно решается?
  
  Представляем функцию стоимости с перекрёстной энтропией
  
  Как нам быть с замедлением обучения?
  Оказывается, мы можем решить проблему,
  заменив квадратичную функцию стоимости другой функцией стоимости,
  известной под названием перекрёстной энтропии.
  Чтобы понять перекрёстную энтропию,
  отойдём от нашей простейшей модели.
  Допустим, мы обучаем нейрон с несколькими входными значениями x1,x2,:
  соответствующими весами w1,w2,: и смещением b:
  
  ......
  
  Выход нейрона, конечно, будет a=?(z),
  где z=?jwjxj+b - взвешенная сумма входов.
  Мы определяем функцию стоимости с перекрёстной энтропией для данного нейрона,
  как
  
  $ C = -\frac{1}{n} \sum_x \left[y \ln a + (1-y ) \ln (1-a) \right] \tag{57} $
  
  где n - общее количество единиц обучающих данных,
  сумма идёт по всем обучающим данным x,
  а y - соответствующий желаемый выход.
  
  Не очевидно, что у равнение (57) решает проблему замедления обучения.
  Честно говоря, даже не очевидно,
  что имеет смысл называть это функцией стоимости!
  Перед тем, как обратиться к замедлению обучения,
  посмотрим, в каком смысле перекрёстную энтропию можно интерпретировать
  как функцию стоимости.
  //// Ну, в принципе никаких особых ограничений,
  //// чтобы назвать самые "хитроумные формулы" функцией стоимости,
  //// как-то "забыли" определить.
  //// Только какие-то общие соображения, причем явно не проговоренные.
  //// И это, на мой взгляд, идеологически правильно,
  //// так надо говорить не о "функции стоимости",
  //// а о функции "управления обучением".
  //// И тогда это уже может указывать на куда более сложную стратегию обучения,
  //// чем просто "реакция" на каждый отдельный обучающий пример.
  //// В принципе те же самые "мини-батчи" можно рассматривать,
  //// как на самую элементарную/зачаточную стратегию обучения.
  
  Два свойства в особенности делают разумным интерпретацию перекрёстной энтропии
  как функции стоимости.
  Во-первых, она больше нуля, то есть, C>0.
  Чтобы увидеть это, отметьте,
  что (а) все отдельные члены суммы в (57) отрицательны,
  поскольку оба логарифма берутся от чисел в диапазоне от 0 до 1,
  и (б) перед суммой стоит знак минуса.
  //// Тоже интересный момент.
  //// Существующая концепция "обратного распространения ошибки"
  //// "по умолчанию" не предполагает "отрицательную стоимость",
  //// т.е. не ошибку/штраф за неправильный результат,
  //// а, наоборот, поощрение/вознаграждение за удачный результат.
  //// Может быть с этим и связана низкая эффективность
  //// "обратного распространения ошибки" в методах "обучения с подкреплением",
  //// т.к. требует очень нетривиальной "конвертации" "поощрения" в "ошибку".
  
  Во-вторых, если реальный выход нейрона близок к желаемому выходу
  для всех обучающих входов x,
  тогда перекрестная энтропия будет близка к нулю.
  Для доказательства нам нужно будет предположить,
  что желаемые выходы y будут равны либо 0, либо 1.
  Обычно так и бывает при решении проблем классификации,
  или вычислении булевых функций.
  Чтобы понять, что происходит, если не делать такого предположения,
  обратитесь к упражнениям в конце раздела.
  
  Чтобы доказать это, представим,
  что y=0 и a?0 for для некоего входного x.
  Так будет, когда нейрон хорошо обрабатывает такой вход.
  Мы видим, что первый член выражения (57)
  для стоимости исчезает, поскольку y=0,
  а второй будет равен ?ln(1?a) ?0.
  То же выполняется, когда y=1 и a?1.
  Поэтому вклад стоимости будет небольшим,
  если реальный выход будет близким к желаемому.
  
  Подытоживая, получим, что перекрёстная энтропия положительна,
  и стремится к нулю, когда нейрон лучше вычисляет желаемый выход y
  для всех обучающих входов x.
  Наличия обоих свойств мы ожидаем у функции стоимости.
  И действительно, оба эти свойства выполняет и квадратичная стоимость.
  Поэтому, для перекрёстной энтропии это хорошие новости.
  Однако у функции стоимости с перекрёстной энтропией есть преимущество,
  поскольку, в отличии от квадратичной стоимости,
  она избегает проблемы замедления обучения.
  Чтобы увидеть это, давайте подсчитаем частную производную стоимости
  с перекрёстной энтропией по весам.
  Подставим a=?(z) в (57), дважды применим цепное правило, и получим:
  
  $ \frac{\partial C}{\partial w_j} = -\frac{1}{n} \sum_x \left( \frac{y }{\sigma(z)} -\frac{(1-y)}{1-\sigma(z)} \right) \frac{\partial \sigma}{\partial w_j} \tag{58} $
  
  $ = -\frac{1}{n} \sum_x \left( \frac{y}{\sigma(z)} -\frac{(1-y)}{1-\sigma(z)} \right)\sigma'(z) x_j \tag{59} $
  
  Приводя к общему знаменателю и упростив, получим:
  
  $ \frac{\partial C}{\partial w_j} = \frac{1}{n} \sum_x \frac{\sigma'(z) x_j}{\sigma(z) (1-\sigma(z))} (\sigma(z)-y). \tag{60} $
  
  Используя определение сигмоиды, ?(z)=1/(1+e?z) и немножко алгебры,
  можно показать, что ??(z) = ?(z)(1 ? ?(z)).
  Проверить это я попрошу вас в упражнении далее,
  но пока что примем это, как истину.
  Члены ??(z) и ?(z)(1??(z)) сокращаются, и это приводит к
  
  $ \frac{\partial C}{\partial w_j} = \frac{1}{n} \sum_x x_j(\sigma(z)-y). \tag{61} $
  
  Прекрасное выражение.
  Из него следует, что скорость, с которой обучаются веса,
  контролируется ?(z)?y, то есть, ошибкой на выходе.
  Чем больше ошибка, тем быстрее обучается нейрон.
  Этого можно было интуитивно ожидать.
  Этот вариант избегает замедления обучения,
  вызванного членом \sigma'(z)
  в аналогичном уравнении для квадратичной стоимости (55).
  Когда мы используем перекрёстную энтропию,
  член \sigma'(z) сокращается,
  и нам уже не приходится волноваться о его малости.
  Это сокращение - особое чудо,
  гарантируемое функцией стоимости с перекрёстной энтропией.
  На самом деле, конечно, это не совсем чудо.
  Как мы увидим позднее,
  перекрёстную энтропию специально выбрали именно за это свойство.
  //// Или сконструировали, под конкретную функцию активации?
  //// Да нет, наверно, это все-таки более общая формула.
  //// А интересно, как она используется для глубинных слоев нейросети?
  
  Сходным образом можно вычислить частную производную для смещения.
  Не буду приводить заново все детали, но можно легко проверить, что
  
  $ \frac{\partial C}{\partial b} = \frac{1}{n} \sum_x (\sigma(z)-y). \tag{62} $
  
  Это опять-таки помогает избежать замедления обучения из-за члена \sigma'(z)
  в аналогичном у равнении для квадратичной стоимости (56).
  
  .....
  
  
  Вернёмся к нашему надуманному примеру, с которым мы игрались ранее,
  и посмотрим, что случится, если вместо квадратичной стоимости
  мы будем использовать перекрёстную энтропию.
  Чтобы настроиться, мы начнём со случая,
  в котором квадратичная стоимость отлично сработала,
  когда начальный вес был 0,6 а смещение - 0,9.
  В оригинале статьи есть интерактивная форма,
  в которой можно нажать кнопку и посмотреть,
  что происходит при замене квадратичной стоимости на перекрёстную энтропию.
  
  Ничего удивительного, нейрон в данном случае обучается прекрасно, как и ранее.
  Теперь посмотрим на случай, в котором раньше нейрон застревал,
  с весом и смещением, начинающимися с величины 2,0.
  
  ......
  
  Успех!
  На этот раз нейрон обучился быстро, как мы и хотели.
  Если наблюдать пристально, можно увидеть,
  что наклон кривой стоимости изначально более крутой,
  по сравнению с плоским регионом соответствующей кривой квадратичной стоимости.
  Эту крутость даёт нам перекрёстная энтропия,
  и не даёт застрять там, где мы ожидаем наискорейшее обучение нейрона,
  когда он начинает с очень больших ошибок.
  
  Я не сказал, какая скорость обучения использовалась в последних примерах.
  Ранее с квадратичной стоимостью мы использовали ?=0,15.
  Должны ли мы использовать такую же скорость и в новых примерах?
  На самом деле, меняя функцию стоимости,
  нельзя точно сказать,
  что значит - использовать <такую же> скорость обучения;
  это будет сравнения яблок с апельсинами.
  Для обеих функций стоимости я экспериментировал,
  подыскивая скорость обучения, позволяющую увидеть происходящее.
  Если вам всё же интересно, то в последних примерах ?=0,005.
  
  Вы можете возразить, что изменение скорости обучения лишает графики смысла.
  Кому какая разница, как быстро обучается нейрон,
  если мы могли произвольно выбирать скорость обучения?
  Но это возражение не учитывает главного.
  Смысл графиков не в абсолютной скорости обучения,
  а в том, как меняется эта скорость.
  При использовании квадратичной функции обучение идёт медленнее,
  если нейрон сильно ошибается,
  а потом идёт быстрее, когда нейрон подходит к нужному ответу.
  С перекрёстной энтропией обучение идёт быстрее,
  когда нейрон сильно ошибается.
  И эти утверждения не зависят от заданной скорости обучения.
  
  Мы исследовали перекрёстную энтропию для одного нейрона.
  Однако это легко обобщить на сети со многими слоями и многими нейронами.
  Допустим, что y=y1, y2,... - желаемые значения выходных нейронов,
  то есть, нейронов в последнем слое,
  а aL1, aL2,.. - сами выходные значения.
  Тогда перекрёстную энтропию можно определить, как:
  
  $ C = -\frac{1}{n} \sum_x \sum_j \left[y_j \ln a^L_j + (1-y_j) \ln (1-a^L_j) \right] \tag{63} $
  
  Это то же самое, что уравнение (57),
  только теперь наша ?j суммирует по всем выходным нейронам.
  Не буду подробно разбирать производную,
  однако разумно предположить, что с использованием выражения (63)
  мы можем избежать замедления в сетях с многими нейронами.
  Если интересно, вы можете взять производную в задаче ниже.
  
  Между прочим, используемый мною термин <перекрёстная энтропия>
  запутал некоторых ранних читателей книги,
  поскольку он противоречит другим источникам.
  В частности, нередко перекрёстную энтропию определяют
  для двух распределений вероятности, pj и qj, как ?jpjlnqj.
  Это определение можно связать с (57),
  если считать один сигмоидный нейрон выдающим распределение вероятности,
  состоящее из активации нейрона a и дополняющей её величины 1-a.
  
  Однако, если у нас в последнем слое много сигмоидных нейронов,
  вектор aLj обычно не даёт распределения вероятностей.
  В итоге определение типа ?jpjlnqj лишено смысла,
  поскольку мы не работаем с распределениями вероятности.
  Вместо этого (63) можно представлять себе,
  как просуммированный набор перекрёстных энтропий каждого нейрона,
  где активация каждого нейрона интерпретируется
  как часть двухэлементного распределения вероятности
  (конечно, в наших сетях нет никаких вероятностных элементов,
  поэтому это на самом деле никакие не вероятности).
  В этом смысле (63) будет обобщением перекрёстной энтропии
  для распределений вероятностей.
  
  Когда использовать перекрёстную энтропию вместо квадратичной стоимости?
  На самом деле, перекрёстную энтропию почти всегда будет использовать лучше,
  если выходные нейроны у вас сигмоидные.
  //// А если сигмоидальны промежуточные нейроны?
  Чтобы понять это, вспомним, что настраивая сеть,
  мы обычно инициализируем веса и смещения при помощи
  некоего случайного процесса.
  Может так получиться, что этот выбор приведёт к тому,
  что сеть будет совершенно неверно интерпретировать
  некие обучающие входные данные
  - к примеру, выходной нейрон будет стремиться к 1,
  когда должен стремиться к 0, или наоборот.
  Если мы используем квадратичную стоимость,
  которая замедляет обучение,
  она не остановит обучение совсем,
  поскольку веса будут продолжать обучаться на других обучающих примерах,
  но такая ситуация, очевидно, является нежелательной.
  
  Упражнения
  
  Один подвох перекрёстной энтропии состоит в том,
  что сначала может быть трудно запомнить соответствующие роли y и a.
  Легко запутаться, как будет правильно,
  -[ylna+(1-y)ln(1-a)] или -[alny+(1-a)ln(1-y)].
  Что будет со вторым выражением, когда y=0 или 1?
  Влияет ли эта проблема на первое выражение?
  Почему?
  
  В обсуждении единственного нейрона в начале раздела,
  я говорил, что перекрёстная энтропия мала,
  если ?(z)?y для всех обучающих входящих данных.
  Аргумент основывался на том, что y равен 0 или 1.
  Обычно в задачах классификации так и есть,
  но в других задачах (например, регрессии)
  y иногда может принимать значения между 0 и 1.
  Покажите, что перекрёстная энтропия всё равно минимизируется,
  когда ?(z)=y для всех обучающих входов.
  Когда так происходит, значение перекрёстной энтропии равно
  $ C = -\frac{1}{n} \sum_x [y \ln y+(1-y) \ln(1-y)] \tag{64} $
  
  Величину -[ylny+(1-y)ln(1-y)] иногда называют бинарной энтропией.
  
  
  Задачи
  
  Многослойные сети с многими нейронами.
  В записи из последнего раздела покажите,
  что для квадратичной стоимости частная производная по весам в выходном слое равна
  
  $ \frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n} \sum_x a^{L-1}_k (a^L_j-y_j) \sigma'(z^L_j) \tag{65} $
  
  Член ?'(zLj) заставляет обучение замедляться,
  когда нейрон склоняется к неверному значению.
  Покажите, что для функции стоимости с перекрёстной энтропией
  выходная ошибка ?L для одного обучающего примера x задаётся уравнением
  
  $ \delta^L = a^L-y \tag{66} $
  
  Используйте это выражение, чтобы показать,
  что частная производная по весам в выходном слое задаётся уравнением
  
  $ \frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n} \sum_x a^{L-1}_k (a^L_j-y_j) \tag{67} $
  
  Член ?'(zLj) исчез,
  поэтому перекрёстная энтропия избегает проблемы замедления обучения,
  не только при использовании с одним нейроном,
  но и в сетях с многими слоями и многими нейронами.
  С небольшим изменением этот анализ подходит и для смещений.
  Если это неочевидно для вас,
  вам лучше проделать и этот анализ также.
  
  Использование квадратичной стоимости с линейными нейронами во внешнем слое.
  Допустим, у нас многослойная сеть с многими нейронами.
  Допустим, в финальном слое все нейроны линейные,
  то есть сигмоидная функция активации не применяется,
  а их выход просто определяется, как aLj = zLj.
  Покажите, что при использовании квадратичной функции стоимости
  выходная ошибка ?L для одного обучающего примера x задаётся
  
  $ \delta^L = a^L-y \tag{68} $
  
  Как и в прошлой задаче, используйте это выражение,
  чтобы показать, что частные производные по весам и смещениям
  во внешнем слое определяются, как
  
  $ \frac{\partial C}{\partial w^L_{jk}} = \frac{1}{n} \sum_x a^{L-1}_k (a^L_j-y_j) \tag{69} $
  
  $ \frac{\partial C}{\partial b^L_{j}} = \frac{1}{n} \sum_x (a^L_j-y_j) \tag{70} $
  
  Это показывает, что если выходные нейроны линейные,
  тогда квадратичная стоимость не вызовет никаких проблем с замедлением обучения.
  В этом случае квадратичная стоимость вполне подходит для использования.
  
  //// Т.е. в отношении выходного слоя требования СОГЛАСОВАНИЯ
  //// функции активации и функции стоимости ошибки
  //// являются вполне разумными, и полностью согласуются с "приемами ТРИЗ".
  
  Использование перекрёстной энтропии для классификации цифр из MNIST
  
  Перекрёстную энтропию легко реализовать в виде части программы,
  обучающей сеть при помощи градиентного спуска и обратного распространения.
  Позже мы сделаем это, разработав улучшенную версию
  нашей ранней программы классификации рукописных цифр из MNIST, network.py.
  Новая программа называется network2.py, и включает
  не только перекрёстную энтропию,
  но и несколько других техник, разработанных в этой главе.
  А пока посмотрим, насколько хорошо наша новая программа
  классифицирует цифры MNIST.
  Как и в главе 1, мы будем использовать сеть с 30 скрытыми нейронами,
  и мини-пакетом размером в 10.
   Установим скорость обучения ?=0,5 и будем обучаться 30 эпох.
  
  Как я уже говорил, нельзя точно сказать,
  какая скорость обучения подойдёт в каком случае,
  поэтому я экспериментировал с подбором.
  Правда, существует способ очень грубо эвристически связать
  скорость обучения с перекрёстной энтропией и квадратичной стоимостью.
  Раньше мы видели, что в членах градиента для квадратичной стоимости
  есть дополнительный член ?'=?(1-?).
  Допустим, мы усредним эти значения для ?, ?10d? ? (1??)=1/6.
  Видно, что (очень грубо) квадратичная стоимость
  в среднем обучается в 6 раз медленнее для той же скорости обучения.
  Это говорит о том, что хорошей отправной точкой будет
  поделить скорость обучения для квадратичной функции на 6.
  Конечно, это совершенно не строгий аргумент,
  и не стоит принимать его слишком серьёзно.
  Но он иногда может быть полезным в качестве отправной точки.
  
  ........
  
  Отметьте, кстати, что команда net.large_weight_initializer()
  используется для инициализации весов и смещений
  так же, как описано в главе 1.
  Нам нужно прогнать её, потому что позже
  мы изменим инициализацию весов по умолчанию.
  В итоге, после запуска всех указанных выше команд,
  мы получим сеть, работающую с точностью в 95,49%.
  Это очень близко к результату из первой главы, 95.42%,
  использовавшему квадратичную стоимость.
  //// Не понял. А где же преимущество "перекрестной энтропии"?
  
  Давайте также посмотрим на случай,
  где мы используем 100 скрытых нейронов и перекрёстную энтропию,
  а всё остальное оставляем таким же.
  В этом случае точность получается 96,82%.
  Это серьёзное улучшение по сравнению с результатами из первой главы,
  где мы достигли точности в 96.59%, используя квадратичную стоимость.
  Изменение может показаться небольшим,
  но подумайте о том, что ошибка упала с 3,41% до 3,18%.
  То есть, мы устранили примерно 1/14 ошибок.
  Это довольно неплохо.
  //// Но "тринадцать из четырнадцати" все-таки остались.
  //// Разве это действительно существенное улучшение?
  
  Довольно приятно, что функция стоимости с перекрёстной энтропией
  даёт нам сходные или лучшие результаты по сравнению с квадратичной стоимостью.
  Однако они не доказывают однозначно, что перекрёстная энтропия - лучший выбор.
  Дело в том, что я вовсе не старался выбирать гиперпараметры
  - скорость обучения, размер мини-пакета, и т.д.
  Чтобы улучшение получилось более убедительным,
  нам нужно как следует заняться их оптимизацией.
  Но результаты всё равно вдохновляющие,
  и подтверждают наши теоретические выкладки о том,
  что перекрёстная энтропия - лучший выбор,
  чем квадратичная функция стоимости.
  
  В этом ключе пройдёт вся эта глава
  и, в принципе, остаток книги.
  Мы будем вырабатывать новую технику, испытывать её,
  и получать <улучшенные результаты>.
  Конечно, хорошо, что мы видим эти улучшения.
  Но интерпретировать их всегда сложно.
  Убедительно будет только,
  если мы увидим улучшения после серьёзной работы по оптимизации
  всех остальных гиперпараметров.
  А это довольно сложная работа,
  требующая больших вычислительных ресурсов,
  и обычно мы не будем заниматься таким тщательным расследованием.
  Вместо этого мы будем идти дальше на базе неформальных тестов,
  как тех, что приведены выше.
  Но вам нужно иметь в виду,
  что такие тесты не являются однозначным доказательством,
  и внимательно следить за теми случаями,
  когда аргументы начинают сбоить.
  
  Пока что мы подробно обсуждали перекрёстную энтропию.
  Зачем тратить столько сил,
  если она даёт такое небольшое улучшение наших результатов по MNIST?
  Позже в этой главе мы увидим другие техники
  - в частности, регуляризацию -
  дающие куда как более сильные улучшения.
  Так зачем мы концентрируемся на перекрёстной энтропии?
  В частности потому, что перекрёстная энтропия
  - это часто используемая функция стоимости,
  поэтому в ней стоит хорошо разобраться.
  Но более важной причиной будет то,
  что насыщение нейронов - важная в области нейросетей проблема,
  к которой мы будем постоянно возвращаться на протяжении книги.
  Поэтому я так подробно обсуждал перекрёстную энтропию,
  поскольку это хорошая лаборатория для того,
  чтобы начать разбираться в насыщении нейронов
  и в том, как можно искать подходы к этой проблеме.
  
  Что означает перекрёстная энтропия?
  Откуда она берётся?
  
  Наше обсуждение перекрёстной энтропии вращалось
  вокруг алгебраического анализа и практической реализации.
  Это полезно, но в итоге остаются без ответа
  более широкие концептуальные вопросы,
  например: что означает перекрёстная энтропия?
  Есть ли интуитивный способ её представлять?
  Как люди вообще могли придумать перекрёстную энтропию?
  
  Начнём с последнего:
  что могло подвигнуть нас на размышление о перекрёстной энтропии?
  Допустим, мы обнаружили замедление обучения, описанное ранее,
   и поняли, что его причиной были члены \sigma'(z) в уравнениях (55) и (56).
  Немного поглазев на эти уравнения, мы могли бы задуматься,
  нельзя ли выбрать такую функцию стоимости,
  чтобы член \sigma''(z) исчез.
  Тогда стоимость C = Cx одного обучающего примера удовлетворяла бы уравнениям:
  
  $ \frac{\partial C}{\partial w_j} = x_j(a-y) \tag{71} $
  
  $ \frac{\partial C}{\partial b } = (a-y) \tag{72} $
  
  Если бы мы выбрали функцию стоимости, делающую их истинными,
  то они бы довольно просто описали бы интуитивное понимание того,
  что чем больше изначальная ошибка, тем быстрее обучается нейрон.
  Они бы также устранили проблему с замедлением обучения.
  На самом деле, начав с этих уравнений, мы бы показали,
  что возможно вывести форму перекрёстной энтропии,
  просто следуя математическому чутью.
  Чтобы увидеть это, отметим, что, исходя из цепного правила, мы получаем:
  
  $ \frac{\partial C}{\partial b} = \frac{\partial C}{\partial a} \sigma'(z) \tag{73} $
  
  Используя в последнем уравнении \sigma'(z) = \sigma(z)(1-\sigma(z)) = a(1-a),
  получим:
  
  $ \frac{\partial C}{\partial b} = \frac{\partial C}{\partial a} a(1-a) \tag{74} $
  
  Сравнив с уравнением (72), получим:
  
  $ \frac{\partial C}{\partial a} = \frac{a-y}{a(1-a)} \tag{75} $
  
  Интегрируя это выражение по a, получим:
  
  $ C = -[y \ln a + (1-y) \ln (1-a)]+ {\rm constant} \tag{76} $
  
  Это вклад отдельного обучающего примера x в функцию стоимости.
  Чтобы получить полную функцию стоимости,
  нам нужно усреднить по всем обучающим примерам, и мы приходим к:
  
  $ C = -\frac{1}{n} \sum_x [y \ln a +(1-y) \ln(1-a)] + {\rm constant} \tag{77} $
  
  Константа тут - средняя по отдельным константам каждого из обучающих примеров.
  Как видим, уравнения (71) и (72) уникальным образом определяют
  форму перекрёстной энтропии,
  вплоть до общей константы.
  Перекрёстную энтропию не доставали волшебным образом из воздуха.
  Её можно было найти простым и естественным образом.
  
  Что насчёт интуитивного представления о перекрёстной энтропии?
  Как нам её себе представлять?
  Подробное объяснение привело бы к тому,
  что мы бы обогнали наш учебный курс.
  Однако можно упомянуть о существовании
  стандартного способа интерпретации перекрёстной энтропии,
  происходящего из области теории информации.
  Грубо говоря, перекрёстная энтропия - это мера неожиданности.
  //// Обязательно надо запомнить.
  Например, наш нейрон пытается подсчитать функцию xy=y(x).
  Но вместо этого он подсчитывает функцию xa=a(x).
  Допустим, мы представляем себе a,
  как оценку нейроном вероятности того, что y=1,
  а 1-a - вероятность того, что правильным значением для y будет 0.
  Тогда перекрёстная энтропия измеряет то,
  насколько мы <удивлены>, в среднем,
  когда обнаруживаем истинное значение y.
  Мы не сильно удивлены, если выход нами ожидаем,
  и сильно удивлены, если выход неожиданный.
  Конечно, я не давал строгого определения <удивлению>,
  поэтому всё это можно показаться пустым разглагольствованием.
  Но на самом деле в теории информации существует
  точный способ определения неожиданности.
  К сожалению, мне неизвестно ни одного примера хорошего,
  короткого и самодостаточного обсуждения этого момента в интернете.
  Но если вам интересно копнуть поглубже,
  то в статье в Википедии есть хорошая общая информация,
  которая отправит вас в нужном направлении.
  Детали можно почерпнуть из 5-й главы, касающейся неравенства Крафта,
  в книжке по теории информации [англ.].
  
  Задача
  
  Мы подробно обсудили замедление обучения,
  которое может произойти при насыщении нейронов в сетях
  с использованием квадратичной функции стоимости при обучении.
  Ещё один фактор, способный подавлять обучение
  - наличие члена xj в уравнении (61).
  Из-за него, когда выход xj приближается к нуля,
  соответствующий вес wj будет обучаться медленно.
  Поясните, почему невозможно устранить член xj
  выбором какой-нибудь хитроумной функции стоимости.
  //// Объяснить не могу, да и не сильно хочется,
  //// а вот новую задачку для себя для метода обратного распространения,
  //// похоже, увидел.
  //// Но она, по-видимому, вообще "родимое пятно" самой архитектуры
  //// сигмоидного нейрона.
  
  Softmax (функция мягкого максимума)
  
  В данной главе мы по большей части будем использовать
  функцию стоимости с перекрёстной энтропией
  для решения проблем замедления обучения.
  Однако мне хочется кратко обсудить ещё один подход к этой проблеме,
  основанный на т.н. softmax-слоях нейронов.
  Мы не будем использовать Softmax-слои в оставшейся части главы,
  поэтому, если вы очень торопитесь, можете пропустить этот раздел.
  Однако в Softmax всё же стоит разобраться,
  в частности, потому, что она интересна сама по себе,
  и в частности потому, что мы будем использовать Softmax-слои в главе 6,
  в нашем обсуждении глубоких нейросетей.
  
  Идея Softmax состоит в том,
  чтобы определить новый тип выходного слоя для НС.
  Он начинается так же, как сигмоидный слой,
  с формирования взвешенных входов
  $z^L_j = \sum_{k} w^L_{jk} a^{L-1}_k + b^L_j$.
  Однако мы не применяем сигмоиду для получения ответа.
  В Softmax-слое мы применяем Softmax-функцию к zLj.
  Согласно ей, активация aLj выходного нейрона ?j равна:
  
  $ a^L_j = \frac{e^{z^L_j}}{\sum_k e^{z^L_k}} \tag{78} $
  
  где в знаменателе мы суммируем по всем выходным нейронам.
  
  Если Softmax-функция вам незнакома,
  уравнение (78) покажется вам загадочным.
  Совершенно неочевидно, зачем нам использовать такую функцию.
  Также неочевидно,
  что она поможет нам решить проблему замедления обучения.
  Чтобы лучше понять уравнение (78),
  предположим, что у нас есть сеть с четырьмя выходными нейронами
  и четырьмя соответствующими взвешенными входами,
  которые мы будем обозначать, как zL1, zL2, zL3 и zL4.
  В оригинале статьи имеются интерактивные регулировочные ползунки,
  которым назначены возможные значения взвешенных входов
  и график соответствующих выходных активаций.
  Хорошей отправной точкой для их изучения будет
  использование нижнего ползунка для увеличения zL4.
  
  ......
  
  Увеличивая zL4, можно наблюдать увеличение соответствующей выходной активации, aL4,
  и уменьшение остальных выходных активаций.
  При уменьшении zL4 aL4 будет уменьшаться,
  а все остальные выходные активации - увеличиваться.
  Присмотревшись, вы увидите, что в обоих случаях
  общее изменение других активаций точно компенсирует изменение,
  происходящее в aL4.
  Причина этого в наличии гарантии того,
  что все выходные активации в сумме дают 1,
  что мы можем доказать при помощи уравнения (78) и кое-какой алгебры:
  
  $ \sum_j a^L_j = \frac{\sum_j e^{z^L_j}}{\sum_k e^{z^L_k}} = 1 \tag{79} $
  
  В итоге, с увеличением aL4 остальные выходные активации
  обязаны уменьшаться на то же значение в сумме,
  чтобы гарантировать, что сумма всех выходных активаций будет равной 1.
  И, конечно, сходные утверждения будут верны для всех остальных активаций.
  
  Из уравнения (78) также следует, что все выходные активации положительны,
  поскольку функция экспоненты положительна.
  Сочетая это с наблюдением из предыдущего параграфа,
  получим, что выход Softmax-слоя будет набором положительных чисел,
  дающих в сумме 1.
  Иначе говоря, выход Softmax-слоя можно представить,
  как распределение вероятности.
  
  То, что выход Softmax-слоя - это распределение вероятности,
  весьма приятно.
  //// Но никак не обосновано кроме "сумме равной единице".
  Во многих задачах удобно иметь возможность
  интерпретировать
  //// Вот именно "интерпретировать", т.е. "предполагать",
  //// и пытаться делать из этого какие=то выводы.
  //// Иногда забывая при этом, что само это "предположение"
  //// подчиняется некоторому "распределению вероятности",
  //// т.е. в конкретном случае может быть очень далеким
  //// от "действительного положения вещей".
  выходные активации aLj как оценку сетью вероятности того,
  что правильным вариантом булет j.
  Так что, к примеру в задаче классификации MNIST
  мы можем интерпретировать aLj как оценку сетью вероятности того,
  что правильным вариантом классификации цифры будет j.
  
  И наоборот, если выходной слой был сигмоидным,
  тогда мы определённо не можем предполагать,
  что активации формируют распределение вероятности.
  Я не буду доказывать это строго,
  но разумно считать, что активации сигмоидного слоя
  в общем случае не формируют распределение вероятности.
  Поэтому с использованием сигмоидного выходного слоя
  мы не получим такой простой интерпретации выходных активаций.
  
  Упражнение
  
  Составьте пример, показывающий,
  что в сети с сигмоидным выходным слоем выходные активации aLj
  не всегда в сумме дают 1.
  
  Мы начинаем немного разбираться в Softmax-функции и том,
  как ведут себя Softmax-слои.
  Просто чтобы подвести промежуточный итог:
  экспоненты в уравнении (78) гарантируют,
  что все выходные активации будут положительными.
  Сумма в знаменателе уравнения (78) гарантирует,
  что выход Softmax в сумме даст 1.
  Поэтому этот вид уравнения уже не кажется загадочным:
  это естественный способ гарантировать,
  чтобы выходные активации формировали распределение вероятности.
  Softmax можно представить себе,
  как способ изменения масштаба zLj с последующим сжатием их в кучку
  для формирования распределения вероятности.
  
  Упражнения
  
  Монотонность Softmax.
  Покажите, что ?aLj / ?zLk положительна, если j=k,
  и отрицательна, если j?k.
  Как следствие, увеличение zLj
  гарантированно увеличивает соответствующую выходную активацию aLj,
  и уменьшает все остальные выходные активации.
  Мы уже видели это эмпирически на примере ползунков,
  однако данное доказательство будет строгим.
  
  Нелокальность Softmax.
  Приятной особенностью сигмоидных слоёв является то,
  что выход aLj - функция соответствующего взвешенного входа, aLj = ?(zLj).
  Поясните, почему с Softmax-слоем это не так:
  любая выходная активация aLj зависит от всех взвешенных входов.
  
  
  Задача
  
  Инвертирование Softmax-слоя.
  //// Наверно, тут лучше было бы сказать "обращение", а не "инвертирование",
  //// т.к. речь идет обратной трансляции ошибки через этот слой.
  Допустим, у нас есть НС с выходным Softmax-слоем и активации aLj известны.
  Покажите, что соответствующие взвешенные входы имеют форму
  zLj = ln aLj + C,
  где C - константа, не зависящая от j.
  
  Проблема замедления обучения
  
  Мы уже достаточно близко познакомились с Softmax-слоями нейронов.
  Но пока что мы не видели, как Softmax-слои позволяют нам
  решить проблему замедления обучения.
  Чтобы понять это, давайте определим функцию стоимости
  на основе <логарифм-правдоподобия>.
  Мы будем использовать x для обозначения обучающего входа сети,
  и y для соответствующего желаемого выхода.
  Тогда ЛПС, связанная с этим обучающим входом, будет:
  
  $ C \equiv -\ln a^L_y \tag{80} $
  
  Так что, если мы, к примеру, обучаемся на изображениях MNIST,
  и на вход пошло изображение 7, тогда ЛПС будет равной -ln aL7.
  Чтобы понять это интуитивно,
  рассмотрим случай, когда сеть хорошо справляется с распознаванием,
  то есть, уверена, что на входе 7.
  В этом случае она оценит значение соответствующей вероятности aL7
  как близкое к 1,
  поэтому стоимость -ln aL7 будет малой.
  И наоборот, если сеть работает плохо,
  то вероятность aL7 будет меньше,
  а стоимость -ln aL7 будет больше.
  Поэтому ЛПС ведёт себя так, как можно ожидать от функции стоимости.
  
  Что насчёт проблемы замедления обучения?
  Для её анализа вспомним, что главное в замедлении
  - поведение величин ?C/?wLjk и ?C/?bLj.
  Не буду подробно расписывать взятие производной
  - попрошу вас сделать это в задачах,
  но с применением кое-какой алгебры можно показать, что:
  
  $ \frac{\partial C}{\partial b^L_j} = a^L_j-y_j \tag{81} $
  
  $ \frac{\partial C}{\partial w^L_{jk}} = a^{L-1}_k (a^L_j-y_j) \tag{82} $
  
  Я тут немного поиграл с обозначениями, и использую немного не так,
  как в прошлом параграфе.
  Там y обозначало желаемый выход сети
  - то есть, если на выходе <7>, то на входе было изображение 7.
  А в этих уравнениях y обозначает вектор выходных активаций,
  соответствующий 7,
  то есть вектор, у которого все нули, кроме единички в 7-й позиции.
  
  Эти уравнения такие же, как и аналогичные выражения,
  полученные нами в более раннем анализе перекрёстной энтропии.
  Сравните, к примеру, уравнения (82) и (67).
  Это то же уравнение, хотя в последнем проведено
  усреднение по обучающим примерам.
  И, как и в первом случае,
  эти выражения гарантируют отсутствие замедления обучения.
  Полезно представлять себе, что выходной Softmax-слой с ЛПС
  довольно сильно похожи на слой с сигмоидным выходом
  и стоимостью на основе перекрёстной энтропии.
  
  Учитывая их схожесть, что нужно использовать
  - сигмоидный выход и перекрёстную энтропию,
  или Softmax-выход и ЛПС?
  На самом деле, во многих случаях хорошо работают оба подхода.
  Хотя далее в этой главе мы будем использовать сигмоидный выходной слой
  со стоимостью на основе перекрёстной энтропии.
  Позже, в главе 6, мы иногда будем использовать Softmax-выход и ЛПС.
  Причина изменений - сделать некоторые из следующих сетей
  более похожими на сети, встречающиеся в некоторых влиятельных научных работах.
  С более общей точки зрения, Softmax и ЛПС стоит использовать,
  когда вам нужно интерпретировать выходные активации как вероятности.
  Такое нужно не всегда, но может оказаться полезным
  в задачах классификации (типа MNIST),
  куда входят не пересекающиеся классы.
  
  Задачи
  
  Выведите уравнения (81) и (82).
  
  Откуда взялось название Softmax?
  Допустим, мы изменим Softmax-функцию так,
  чтобы выходные активации задавались уравнением
  
  $ a^L_j = \frac{e^{c z^L_j}}{\sum_k e^{c z^L_k}} \tag{83} $
  
  где c - положительная константа.
  Отметим, что c = 1 соответствует стандартной Softmax-функции.
  Но используя другое значение c, мы получим другую функцию,
  которая качественно всё равно будет похожей на Softmax.
  Покажите, что выходные активации формируют распределение вероятности,
  как и в случае с обычной Softmax.
  Допустим, мы сделаем c очень большой, то есть c  &inf;.
  Какого ограничивающее значение выходных активаций aLj?
  После решения этой задачи должно быть ясно,
  почему функция с c = 1 считается <смягчённой версией> функции максимума.
  Отсюда и происходит термин softmax.
  
  Обратное распространение с Softmax и ЛПС.
  В прошлой главе мы вывели алгоритм обратного распространения для сети,
  содержащей сигмоидные слои.
  Чтобы применить этот алгоритм к сети и Softmax-слоями,
  нам надо вывести выражение для ошибки ?Lj ? ?C/?zLj.
  Покажите, что подходящим выражением будет
  
  $ \delta^L_j = a^L_j -y_j \tag{84} $
  
  Используя это выражение,
  мы можем применить алгоритм обратного распространения к сети,
  используя выходной Softmax-слой и ЛПС.
  
  Переобучение и регуляризация
  
  У нобелевского лауреата Энрико Ферми как-то спросили мнение
  по поводу математической модели,
  предложенной несколькими коллегами для решения
  важной нерешённой физической проблемы.
  Модель прекрасно соответствовала эксперименту,
  но Ферми отнёсся к ней скептически.
  Он спросил, сколько свободных параметров в ней можно менять.
  <Четыре>, - сказали ему.
  Ферми ответил:
  <Помню, как мой друг Джонни фон Нейман любил говорить,
  что с четырьмя параметрами туда можно запихнуть и слона,
  а с пятью можно заставить его махать хоботом>.
  
  Смысл истории, конечно, в том,
  что модели с большим количеством свободных параметров
  могут описывать удивительно широкий круг явлений.
  Даже если такая модель хорошо работает с доступными данными,
  это не делает её автоматически хорошей моделью.
  Это просто может означать, что у модели достаточно свободы,
  чтобы она могла описать почти любой набор данных заданного размера,
  не выявляя основной идеи явления.
  Когда это происходит, модель хорошо работает с существующими данными,
  но не сможет обобщить новую ситуацию.
  Истинная проверка модели - её способность делать предсказания
  в ситуациях, с которыми она не сталкивалась ранее.
  
  Ферми и фон Нейман подозрительно относились к моделям с четырьмя параметрами.
  У нашей НС с 30 скрытыми нейронами для классификации цифр MNIST
  есть почти 24 000 параметров!
  Это довольно много параметров.
  У нашей НС с 100 скрытыми нейронами есть почти 80 000 параметров,
  а у передовых глубоких НС этих параметров иногда миллионы или даже миллиарды.
  Можем ли мы доверять результатам их работы?
  
  Давайте усложним эту проблему, создав ситуацию,
  в которой наша сеть плохо обобщает новую для неё ситуацию.
  //// Сеть не "обобщает". Нигде не доказано, что она что-то там обобщает.
  //// Она просто "машет хоботом" в задачах, интерполяции и экстраполяции,
  //// и "машет" по-разному в разных поддиапазонах.
  //// А интерпретация этого как "обобщение", это "некоторое преувеличение",
  //// со своим, причем неизвестным, распределением вероятности.
  //// К сожалению, об этом нужно постоянно напоминать,
  //// так именно такими фразами и "слагаются мифы и легенды"
  //// относительно самых различных свойств нейросетей.
  Мы будем использовать НС с 30 скрытыми нейронами и 23 860 параметрами.
  Но мы не будем обучать сеть при помощи всех 50 000 изображений MNIST.
  Вместо этого используем только первые 1000.
  Использование ограниченного набора сделает проблему обобщения более очевидной.
  Мы будем обучаться так, как и раньше,
  используя функцию стоимости на основе перекрёстной энтропии,
  со скоростью обучения ?=0,5 и размером мини-пакета 10.
  Однако мы будем обучаться 400 эпох,
  что немного больше, чем было раньше,
  поскольку обучающих примеров у нас не так много.
  Давайте используем network2, чтобы посмотреть на то,
  как меняется функция стоимости:
  
  ........
  
  Используя результаты, мы можем построить график изменения стоимости
  при обучении сети (графики сделаны при помощи программы overfitting.py):
  
  Выглядит обнадёживающе, идёт гладкое уменьшение стоимости, как и ожидалось.
  Учтите, что я показал только эпохи с 200 по 399.
  В итоге мы видим в увеличенном масштаба поздние этапы обучения,
  на которых, как мы увидим далее, и происходит всё самое интересное.
  
  Теперь посмотрим, как меняется точность классификации на проверочных данных
  во времени:
  
  .......
  
  Тут я снова увеличил график.
  В первые 200 эпох, которых тут не видно, точность растёт почти до 82%.
  Затем обучение постепенно замедляется.
  Наконец, примерно на 280-й эпохе точность классификации перестаёт улучшаться.
  На поздних эпохах наблюдаются лишь небольшие стохастические флуктуации
  вокруг значения точности, достигнутого на 280-й эпохе.
  Сравните это с предыдущим графиком,
  где стоимость, связанная с обучающими данными, плавно уменьшается.
  Если изучать только эту стоимость,
  то будет казаться, что модель улучшается.
  Однако результаты работы с проверочными данными говорят нам,
  что это улучшение - лишь иллюзия.
  Как и в модели, которая не понравилась Ферми,
  то, что наша сеть изучает после 280-й эпохи,
  уже не обобщается на проверочных данных.
  Поэтому это обучение перестаёт быть полезным.
  Мы говорим, что после 280-й эпохи сеть переобучается,
  или переподгоняется [overfitting or overtraining].
  //// Возможно, что на этом последующем этапе нужно применять
  //// уже какую-то иную методику обучения,
  //// или хотя бы другую "функцию стоимости",
  //// чтобы обучить модель как-то по-иному.
  //// Увеличение обучающего датасета за счет других, например, синтетически данных
  //// это, наверняка, далеко не единственно возможный способ.
  //// Можно ведь попробовать заставить обучаться модель,
  //// подсовывая не совсем "честные" оценки ошибки.
  
  //// Пару дней назад как раз о таком способе прочитал,
  //// когда в оценку ошибки в любом случае добавляют какую-то константу,
  //// и это "почему-то" улучшает качество обучения модели.
  //// Но это как-то противоречит тезису о "обобщающей способности" нейросети.
  //// Что в случае такой константной составляющей ошибки "обобщается"?
  
  Вы можете задуматься над тем, не является ли проблемой то,
  что я изучаю стоимость на основе обучающих данных,
  а не точности классификации проверочных данных.
  Иначе говоря, возможно, проблема в том, что мы сравниваем яблоки с апельсинами.
  Что будет, если мы сравним стоимость обучающих данных со стоимостью проверочных,
  то есть, будем сравнивать сравнимые меры?
  Или, возможно, мы могли бы сравнить точность классификации как обучающих,
  так и проверочных данных?
  На самом деле, то же явление проявляется вне зависимости от того,
  как проводить сравнение.
  Но изменяются детали.
  К примеру, давайте посмотрим стоимость проверочных данных:
  
  .......
  
  Видно, что стоимость проверочных данных улучшается примерно до 15-й эпохи,
  а потом вообще начинает ухудшаться,
  хотя стоимость обучающих данных продолжает улучшаться.
  Это ещё один признак переобученной модели.
  //// Точнее ее более тонкой "настройки" на обучающие данные.
  //// Можно ведь взглянуть на это дело и так,
  //// что нейросеть старается наиболее точно соответствовать нашим требованиям,
  //// оформленным в виде функции стоимости, а вовсе не тому,
  //// что предполагается/хочется от нее получить.
  //// Тут явное техническое противоречие между способом управления обучением,
  //// и желаемым результатом.
  
  //// И тут явно требуется как минимум "двухфазное обучение".
  //// Первая фаза реальное обучение параметров нейросети на примерах датасета.
  //// Так сказать задание каркаса/скелета решений нейросети,
  //// так сказать "закалка заготовки".
  //// А вторая фаза - это попытка "допилить напильником" , так сказать "полировка"
  //// или, может быть лучше, "мягкий отпуск/отжиг заготовки".
  //// под требования интерполяции/экстраполяции.
  
  //// Причем, если продолжить эту аналогию с "обработкой металлов",
  //// то сразу напрашивается даже не "двухфазное",
  //// а "многостадийная обработка",
  //// включая "легирование", "ковку", "нагревание", "охлаждение", "закалку" и т.д.
  //// с РАЗЛИЧНЫМИ параметрами этих процедур.
  
  //// И если для "интерполяции" какое-то частное решение уже найдено,
  //// например, та же самая "добавка", константная или переменная/случайная,
  //// к функции ошибок/стоимости,
  //// то для "экстраполяции" таких приемов, вроде, еще не найдено.
  
  Однако встаёт вопрос, какую эпоху мы должны считать точкой,
  в которой переобучение начинает преобладать над обучением - 15 или 280?
  С практической точки зрения нас всё же интересует
  улучшение точности классификации проверочных данных,
  а стоимость - это всего лишь посредник точности классификации.
  Поэтому имеет смысл считать эпоху 280 точкой,
  после которой переобучение начинает преобладать над обучением нашей НС.
  
  Ещё один признак переобучения можно увидеть
  в точности классификации обучающих данных:
  
  ........
  
  Точность растёт, доходя до 100%.
  То есть, наша сеть правильно классифицирует все 1000 обучающих изображений!
  Тем временем, проверочная точность вырастает лишь до 82,27%.
  То есть, наша сеть лишь изучает особенности обучающего набора,
  а не учится распознавать цифры вообще.
  Похоже на то, что сеть просто запоминает обучающий набор,
  //// Точнее, подстраивается под процесс обучения.
  //// И если хочется что-то изменить в получающемся результате,
  //// то, скорее всего, нужно вносить изменение в процесс обучения.
  недостаточно хорошо поняв цифры для того,
  чтобы обобщить это на проверочный набор.
  //// Вот нагляднейший пример того, как миф "обобщающей способности нейросети"
  //// создает устойчивую психологическую инерцию,
  //// не позволяющую как-то по иному, взглянуть на суть проблемы.
  
  Переобучение - серьёзная проблема НС.
  Особенно это верно для современных НС,
  в которых обычно есть огромное количество весов и смещений.
  Для эффективного обучения нам нужен способ определять,
  когда возникает переобучение, чтобы не переобучать.
  //// Да не "возникает переобучение", а поведение нейросети,
  //// отражает методику обучения.
  //// И происходит это всегда, начиная с самого первого обучающего примера,
  //// но если в начале обучения такое поведение нейросети всячески приветствуется,
  //// см. разделы про ускорение обучения,
  //// то на финишном этапе это как раз и составляет проблему.
  //// "Нет худа без добра" и наоборот.
  //// И тут претензия не к нейросети, а к "однобокости", "негибкости"
  //// методики обучения.
  //// Не с "переобучением" нейросети надо бороться,
  //// а с недостаточной гибкостью методики обучения.
  А ещё нам хотелось бы уметь уменьшать эффекты переобучения.
  
  Очевидный способ обнаружить переобучение - использовать подход выше,
  следить за точностью работы с проверочными данными в процессе обучения сети.
  Если мы увидим, что точность на проверочных данных уже не улучшается,
  надо прекращать обучение.
  //// Или менять функцию ошибки/стоимости.
  Конечно, строго говоря, это не обязательно будет признаком переобучения.
  Возможно, точность работы с проверочными и с обучающими данными
  прекратят улучшаться одновременно.
  И всё же применение такой стратегии предотвратит переобучение.
  //// Не-а. "Рожденный ползать - летать не может".
  //// Такое явление как "adversarial attack" этот подход точно исключить не может.
  //// Хотя причиной этого явления точно также считается "переобучение".
  
  А мы будем использовать небольшую вариацию этой стратегии.
  Вспомним, что когда мы загружаем в MNIST данные, мы делим их на три набора:
  
  >>> import mnist_loader
  >>> training_data, validation_data, test_data = \
  ... mnist_loader.load_data_wrapper()
  
  Пока что мы использовали training_data и test_data,
  и игнорировали validation_data [подтверждающие].
  В validation_data содержится 10 000 изображений,
  отличающихся как от 50 000 изображений обучающего набора MNIST,
  так и от 10 000 изображений проверочного набора.
  Вместо использования test_data для предотвращения переобучения
  мы будем использовать validation_data.
  //// А в чем разница?
  Для этого мы будем использовать практически ту же стратегию,
  что была описана выше для test_data.
  То есть, мы будем вычислять точность классификации validation_data
  в конце каждой эпохи.
  Как только точность классификации validation_data насытится,
  мы перестанем обучаться.
  Эта стратегия называется ранней остановкой.
  Конечно, на практике мы не сможем сразу же узнать, что точность насытилась.
  Вместо этого мы будем продолжать обучение,
  пока не удостоверимся в этом
  (причём решить, когда нужно остановиться, не всегда просто,
  и можно использовать для этого более или менее агрессивные подходы).
  
  Зачем использовать validation_data для предотвращения переобучения,
  а не test_data?
  Это часть более общей стратегии
  - использовать validation_data для оценки
  разных вариантов выбора гиперпараметров
  - количества эпох для обучения, скорости обучения, наилучшей архитектуры сети, и т.д.
  Эти оценки мы используем, чтобы найти и присвоить хорошие значения гиперпараметрам.
  И хотя я пока ещё этого не упоминал,
  частично благодаря этому я и сделал выбор гиперпараметров
  в ранних примерах в книге.
  
  Конечно, это замечание не отвечает на вопрос о том,
  почему мы используем validation_data, а не test_data,
  для предотвращения переобучения.
  Оно просто заменяет ответ на более общий вопрос
  - почему мы используем validation_data, а не test_data,
  для выбора гиперпараметров?
  Чтобы понять это, учтите, что при выборе гиперпараметров
  нам, скорее всего, придётся выбирать из множества их вариантов.
  Если мы будем назначать гиперпараметры на основе оценок из test_data,
  мы, возможно, слишком сильно подгоним эти данные именно под test_data.
  То есть, мы, возможно, найдём гиперпараметры,
  хорошо подходящие под особенности конкретных данных из test_data,
  однако работа нашей сети не будет обобщаться на другие наборы данных.
  Этого мы избегаем, подбирая гиперпараметры при помощи validation_data.
  А потом, получив нужные нам ГП,
  мы проводим итоговую оценку точности с использованием test_data.
  Это даёт нам уверенность в том,
  что наши результаты работы с test_data
  являются истинной мерой степени генерализации НС.
  Иначе говоря, подтверждающие данные
  - это такие особые обучающие данные,
  помогающие нам обучиться хорошим ГП.
  Такой подход к поиску ГП иногда называют методом удержания,
  поскольку validation_data <удерживают> отдельно от training_data.
  
  На практике, даже после оценки качества работы на test_data
  мы захотим поменять наше мнение и попробовать другой подход
  - возможно, другую архитектуру сети -
  который будет включать поиски нового набора ГП.
  В этом случае не возникнет ли опасность того,
  что мы излишне приспособимся к test_data?
  Не потребуется ли нам потенциально бесконечное количество наборов данных,
  чтобы мы были уверены в том,
  что наши результаты хорошо обобщаются?
  В целом это глубокая и сложная проблема.
  Но для наших практических целей мы не будем
  слишком сильно переживать по этому поводу.
  Мы просто нырнём с головой в дальнейшие исследования,
  используя простой метод удержания
  на основе training_data, validation_data и test_data,
  как описано выше.
  
  Пока что мы рассматривали переобучение с использованием 1000 обучающих изображений.
  Что будет, если мы используем полный обучающий набор из 50 000 изображений?
  Все остальные параметры мы оставим без изменений
  (30 скрытых нейронов, скорость обучения 0,5, размер мини-пакета 10),
  но будем обучаться 30 эпох с использованием всех 50 000 картинок.
  Вот график, на котором показана точность классификации на обучающих данных
  и проверочных данных.
  Отметьте, что здесь я использовал проверочные [test],
  а не подтверждающие [validation] данные,
  чтобы результаты было проще сравнивать с более ранними графиками.
  
  ......
  
  Видно, что показатели точности на проверочных и обучающих данных
  остаются ближе друг к другу, чем при использовании 1000 обучающих примеров.
  В частности, наилучшая точность классификации, 97,86%,
  всего на 2,53% выше, чем 95,33% проверочных данных.
  Сравните с ранним разрывом в 17,73%!
  Переобучение происходит, но сильно уменьшилось.
  Наша сеть гораздо лучше обобщает информацию,
  переходя с обучающих на проверочные данные.
  В целом, один из лучших способов уменьшения переобучения
  - увеличение объёма обучающих данных.
  //// А какие еще есть способы?
  //// А есть ли какие-то ограничения на объем обучающих данных,
  //// от "количества параметров"?
  Взяв достаточно обучающих данных,
   сложно переобучить даже очень крупную сеть.
  //// А очень малую?
  К сожалению, получить обучающие данные бывает дорого и/или сложно,
  поэтому такой вариант не всегда оказывается практичным.
  
  Регуляризация
  
  Увеличение количества обучающих данных
  - один из способов уменьшения переобучения.
  Есть ли другие способы уменьшения проявлений переобучения?
  Один из возможных подходов - уменьшение размера сети.
  Правда, у больших сетей возможностей потенциально больше чем у малых,
  поэтому к такому варианту мы прибегаем неохотно.
  
  К счастью, существуют и другие техники, способные уменьшить переобучение,
  даже когда у нас фиксированы размер сети и обучающих данных.
  Они известны, как техники регуляризации.
  В данной главе я опишу одну из наиболее популярных техник,
  которую иногда называют ослаблением весов,
  или регуляризацией L2.
  Её идея в том, чтобы добавить к функции стоимости дополнительный член
  под названием член регуляризации.
  //// Оказывается, я опять "изобретаю велосипед".
  Вот перекрёстная энтропия с регуляризацией:
  
  $ C = -\frac{1}{n} \sum_{xj} \left[ y_j \ln a^L_j+(1-y_j) \ln (1-a^L_j)\right] +
  \frac{\lambda}{2n} \sum_w w^2 \tag{85} $
  
  Первый член - обычное выражение для перекрёстной энтропии.
  Но мы добавили второй, а именно, сумму квадратов всех весов сети.
  Он масштабируется множителем ?/2n,
  где ?>0 - это параметр регуляризации,
  а n, как обычно - размер обучающего набора.
  Мы обсудим, как выбрать ?.
  //// Не, это немного не то. Это "одноколесный велосипед",
  //// а я "изобретал многоколесный".
  Также стоит отметить, что в член регуляризации не входят смещения.
  Об этом ниже.
  
  Конечно, возможно регуляризировать и другие функции стоимости,
  например, квадратичную.
  Это можно сделать схожим образом:
  
  $ C = \frac{1}{2n} \sum_x \|y-a^L\|^2 + \frac{\lambda}{2n} \sum_w w^2 \tag{86} $
  
  В обоих случаях можно записать регуляризированную функцию стоимости, как
  
  $ C = C_0 + \frac{\lambda}{2n} \sum_w w^2 \tag{87} $
  
  где C0 - оригинальная функция стоимости без регуляризации.
  
  Интуитивно ясно, что смысл регуляризации склонить сеть к предпочтению более малых весов, при прочих равных.
  //// А чем такое "склонение" в более предпочтительно?
  Крупные веса будут возможны,
  только если они значительно улучшают первую часть функции стоимости.
  Иначе говоря, регуляризация - это способ выбора компромисса
  между нахождением малых весов и минимизацией изначальной функции стоимости.
  Важно, что эти два элемента компромисса зависят от значения ?:
   когда ? мала, мы предпочитаем минимизировать оригинальную функцию стоимости,
  а когда ? велика, то предпочитаем малые веса.
  
  Совершенно не очевидно, почему выбор подобного компромисса
  должен помочь уменьшить переобучение!
  Но оказывается, помогает.
  //// Оно и должно помогать, хотя бы потому,
  //// что на один и тот же обучающий пример в разных эпохах
  //// будет добавлено к ошибке/стоимости разные значения.
  //// Этакое "помешивание в бокале".
  //// А вот нужно ли это "перемешивание" привязывать к весам параметров,
  //// мне кажется, вопрос достаточно дискуссионный.
  Мы разберёмся в том, почему он помогает, в следующей секции.
  Но сначала давайте поработаем с примером,
  показывающим, что регуляризация действительно уменьшает переобучение.
  
  Чтобы сконструировать пример, сначала нам нужно понять,
  как применить обучающий алгоритм со стохастическим градиентным спуском
  к регуляризованной НС.
  В частности, нам надо знать, как подсчитывать частные производные,
  ?C/?w и ?C/?b для всех весов и смещений в сети.
  После взятия частных производных в уравнении (87) получим:
  
  $ \frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w} + \frac{\lambda}{n} w \tag{88} $
  
  $ \frac{\partial C}{\partial b} = \frac{\partial C_0}{\partial b} \tag{89} $
  
  Члены ?C0/?w и ?C0/?w можно вычислить через ОР, как описано в предыдущей главе.
  Мы видим, что подсчитать градиент регуляризованной функции стоимости легко:
  просто нужно, как обычно, использовать ОР,
  а потом добавить ?/n w к частной производной всех весовых членов.
  Частные производные по смещениям не меняются,
  поэтому правило обучения градиентным спуском для смещений
  не отличается от обычного:
  
  $ b \rightarrow b -\eta \frac{\partial C_0}{\partial b} \tag{90} $
  
  Правило обучения для весов превращается в:
  
  $ w \rightarrow w-\eta \frac{\partial C_0}{\partial w}-\frac{\eta \lambda}{n} w \tag{91} $
  
  $ = \left(1-\frac{\eta \lambda}{n}\right) w -\eta \frac{\partial C_0}{\partial w} \tag{92} $
  
  Всё то же самое, что и в обычном правиле градиентного спуска,
  кроме того, что мы сначала масштабируем вес w на множитель 1 - ??/n.
  Это масштабирование иногда называют ослаблением весов,
  поскольку оно уменьшает веса.
  На первый взгляд кажется, что веса неудержимо стремятся к нулю.
  Но это не так, поскольку другой член может привести к увеличению весов,
  если это приводит к уменьшению нерегуляризованной функции стоимости.
  //// Не знаю, может быть я чего-то не понимаю,
  //// но в этом подходе с регуляризацией весов через их "ослабление",
  //// мне видится определенное противоречие в логике обучения.
  //// С одной стороны представления о том как данный "вес" оказывает влияние
  //// на ВСЕ поведение нейросети для ВСЕХ обучающих примеров точно неизвестно,
  //// следовательно, его априорное уменьшение по какому-то абстрактному критерию,
  //// это, вообще-то, игра "в орлянку" т.е. "фифти-фифти".
  //// С другой стороны, это действие как-то вообще игнорирует
  //// так распиаренную "генерализующую способность нейросети",
  //// т.е. тупо по чуть-чуть меняем все веса,
  //// с верой в то, что все как-то само "рассосется" в нужном направлении.
  //// Т.е. налицо определенное "двоемыслие".
  
  //// Но характерно, что это дает какой-то эффект.
  //// И тогда вопрос надо ставить так - а за счет чего это может происходить.
  //// Может быть дело не "в уменьшении весов", а их стохастическом изменении,
  //// которое вполне может быть достигнуто и другими способами,
  //// изменив методику обучения.
  
  Хорошо, пусть градиентный спуск работает так.
  Что насчёт стохастического градиентного спуска?
  Ну, как и в нерегуляризованном варианте стохастического градиентного спуска,
  мы можем оценить ?C0/?w через усреднение по мини-пакету m обучающих примеров.
  Поэтому регуляризованное правило обучения
  для стохастического градиентного спуска превращается в (см. уравнение (20)):
  
  $ w \rightarrow \left(1-\frac{\eta \lambda}{n}\right) w -\frac{\eta}{m} \sum_x \frac{\partial C_x}{\partial w} \tag{93} $
  
  где сумма идёт по обучающим примерам x в мини-пакете,
  а Cx - нерегуляризованная стоимость для каждого обучающего примера.
  Всё то же самое, что и в обычно правиле стохастического градиентного спуска,
  за исключением 1 - ??/n, фактора ослабления веса.
  Наконец, для полноты картины,
  позвольте записать регуляризованное правило для смещений.
  Оно, естественно, точно такое же, как и в нерегуляризованном случае
  (см. уравнение (21)):
  
  $ b \rightarrow b - \frac{\eta}{m} \sum_x \frac{\partial C_x}{\partial b} \tag{94} $
  
  где сумма идёт по обучающим примерам x в мини-пакете.
  
  Давайте посмотрим, как регуляризация меняет эффективность нашей НС.
  Мы будем использовать сеть с 30 скрытыми нейронами, мини-пакет размера 10,
  скорость обучения 0,5, и функцию стоимости с перекрёстной энтропией.
  Однако на этот раз мы используем параметр регуляризации ?=0,1.
  В коде я назвал эту переменную lmbda,
  поскольку слово lambda зарезервировано в python
  для не связанных с нашей темой вещей.
  Я также снова использовал test_data again вместо validation_data.
  Но я решил использовать test_data,
  поскольку результаты можно сравнивать напрямую с нашими ранними,
  нерегуляризованными результатами.
  Вы легко можете поменять код так, чтобы он использовал validation_data,
  и убедиться, что результаты получаются схожими.
  
  .......
  
  Стоимость обучающих данных постоянно уменьшается,
  как и в раннем случае, без регуляризации:
  
  .......
  
  Но на этот раз точность на test_data продолжает увеличиваться
  в течение всех 400 эпох:
  
  .......
  
  Очевидно, регуляризация подавила переобучение.
  Более того, точность значительно возросла,
  и пиковая точность классификации достигает 87,1%, по сравнению с пиком 82,27%,
  достигнутым в случае без регуляризации.
  И вообще, мы почти наверняка достигаем лучших результатов,
  продолжая обучение после 400 эпох.
  Судя по всему, эмпирически,
  регуляризация заставляет нашу сеть лучше обобщать знания,
  //// Да не "обобщать", а подстраиваться под режим обучения.
  //// Нейросеть с градиентным спуском обратным распространением ошибки
  //// - это алгоритм подстройки параметров, а не алгоритм "обобщения".
  //// И он делает ровно то, что ему предписано, и "ни на миллиметр больше".
  //// А "магия обобщения" - это очень вредный миф,
  //// хотя и такой приятный для слуха.
  //// Который слишком часто заканчивается не очень хорошо,
  //// если слепо на него полагаться.
  и значительно уменьшает эффекты переобучения.
  //// И опять же не "переобучение", а, как бы лучше подобрать термин,
  //// а эффект "крупнозернистой структуры",
  //// соответствующий "зернистости" обучающих данных.
  //// Регуляризация в данном случае выступает как процесс "дробления"
  //// более крупных "зерен", на более "мелкие".
  //// Переводит "крупнодисперсную" структуру решений нейросети,
  //// в более "мелкодисперную".
  //// Отсюда и улучшение результатов на тестовой выборке.
  
  //// Если опять же вернуться к аналогии с "обработкой металлов",
  //// то это процесс аналогичный ...
  //// ультразвуковой или электромагнитной обработке процессов литья,
  //// когда таким воздействием повышается "дисперсность" структуры металла,
  //// предотвращается возникновение внутренних трещин, крупных кристаллов/зерен,
  //// и много чего еще - зависит от техпроцесса.
  //// Вот такая аналогия, мне кажется более практичной,
  //// чем "мантры" про "магическую способность обобщения" нейросетей,
  //// и "страшилки" про переобучение.
  
  Что произойдёт, если мы оставим наше искусственное окружение,
  в котором используется всего 1 000 обучающих картинок,
  и вернёмся к полному набору из 50 000 изображений?
  Конечно, мы уже увидели, что переобучение представляет
  куда как меньшую проблему с полным набором из 50 000 изображений.
  Помогает ли улучшить результат регуляризация?
  Давайте оставим прежние значения гиперпараметров
  - 30 эпох, скорость 0,5, размер мини-пакета 10.
  Однако, нам нужно поменять параметр регуляризации.
  Дело в том, что размер n обучающего набора скакнул от 1000 до 50 000,
  а это меняет фактор ослабления весов 1 - ??/n.
  Если мы продолжим использовать ?=0,1,
  это означало бы, что веса ослабляются куда меньше,
  и в итоге эффект от регуляризации уменьшается.
  Мы компенсируем это, приняв ?=5,0.
  
  Хорошо, давайте обучим нашу сеть, сначала повторно инициализировав веса:
  
  .......
  
  Мы получаем результаты:
  
  .......
  
  Много всего приятного.
  Во-первых, наша точность классификации на проверочных данных подросла,
  с 95,49% без регуляризации до 96,49% с регуляризацией.
  Это серьёзное улучшение.
  //// "Семь из девяти ошибок остается".
  //// Кто рискнет такую систему использовать
  //// в контуре управления безопасностью?
  Во-вторых, можно видеть, что разрыв между результатами работы
  на обучающем и проверочном наборах гораздо ниже, чем раньше, менее 1%.
  Разрыв всё равно приличный,
  но мы, очевидно, достигли значительного прогресса в уменьшении переобучения.
  ///// Но до требования "5 девяток" еще "палкой не докинуть",
  ///// а для обычных требований безопасности надо вообще "девять девяток",
  ///// совершенно ясно, что такой подход не даст таких решений
  ///// вряд ли когда-нибудь, если ничего не менять "в консерватории".
  
  Наконец, посмотрим, какую точность классификации мы получим
  при использовании 100 скрытых нейронов и параметра регуляризации &lambda=5,0.
  Не буду приводить подробный анализ переобучения,
  это делается просто ради интереса,
  чтобы посмотреть, насколько большой точности можно достигнуть
  с нашими новыми хитростями:
  функцией стоимости с перекрёстной энтропией и регуляризацией L2.
  
  ........
  
  Конечный результат - точность классификации в 97,92% на подтверждающих данных.
  Большой скачок по сравнению со случаем с 30 скрытыми нейронами.
  Можно подстроить ещё немного, запустить процесс на 60 эпох с ?=0,1 и ?=5,0,
  и преодолеть барьер в 98%,
  достигнув точность 98,04 на подтверждающих данных.
  Неплохо для 152 строк кода!
  
  Я описал регуляризацию как способ уменьшения переобучения
  и увеличения точности классификации.
  Но это не единственные её преимущества.
  Эмпирически, испробовав через множество запусков нашу сеть MNIST,
  меняя каждый раз веса, я обнаружил,
  что запуски без регуляризации иногда <застревали>,
  очевидно, попав в локальный минимум функции стоимости.
  В итоге разные запуски иногда выдавали сильно разные результаты.
  А регуляризация, наоборот, позволяет получать
  гораздо легче воспроизводимые результаты.
  //// Это все тот же вопрос с "дисперсности" "обучающих данных" и метода обучения,
  //// но это отдельная большая тема.
  //// Но сам факт примечательный, и его запомнить.
  //// "Играя в орлянку" с весами нейросети,
  //// "локальные минимумы" оказываются не так страшны.
  
  Почему так получается?
  Эвристически, когда у функции стоимости нет регуляризации,
  длина вектора весов, скорее всего, будет расти, при прочих равных.
  Со временем это может привести к очень большому вектору весов.
  А из-за этого вектор весов может застрять,
  показывая примерно в одном и том же направлении,
  поскольку изменения из-за градиентного спуска
  делают лишь крохотные изменения в направлении при большой длине вектора.
  Я считаю, что из-за этого явления нашему алгоритму обучения
  очень тяжело как следует изучить пространство весов,
  и, следовательно, тяжело найти хороший минимум функции стоимости.
  //// Вообще-то. Такого рода заключения хорошо бы сопроводить иллюстрацией.
  //// Например, распределением весов. Это так легко сделать.
  //// Или сделать "регуляризацию наоборот" - увеличивая веса,
  //// или вообще менять чуть-чуть случайно,
  //// и показать, что такая "регуляризация", точнее "хаотизация",
  //// точно/гарантированно НЕ работает.
  
  //// Но почему-то таких работ никто не считает нужным либо делать,
  //// либо публиковать.
  //// Может быть потому, что такие практические результаты,
  //// "не совсем" согласуется с "красивой" и "правильной" теорией.
  
  //// А вот если все-таки рассматривать предложенную регуляризацию,
  //// просто как способ повышения "мелкодисперности" решения,
  //// без относительности направления уменьшения/увеличения весов,
  //// то способность преодолевать "локальные минимумы" в "функции стоимости"
  //// как-то не кажется слишком загадочной.
  
  .......
  
  //// Из комментариев.
  
  spiral17
  21 авг 2019 в 21:27
  
  Да, спасибо! С темой знаком достаточно давно,
  но все равно обнаружил неизвестные мне вещи из-за того,
  что автор разбирает тему подробно до болтов.
  
  .......
  
  //// Конец цитирования.
  
  В общем, слепая вера в "магические свойства" нейросетей,
  мифы об их "обобщающих свойствах" и "опасности переобучения",
  в сумме не позволяют подойти к backpropagation
  просто как основы нормального многоступенчатой технологии
  получения требуемого результата.
  А не упования на какую-то "единственно верную"
  или идеальную функцию ошибки/стоимости.
  
  
  =======
  
  02.04.2024 21:39
  
  Пара бит про "переобучение".
  В оглавление.
  
  Буквально вчера на Хабре появилась сама по себе интересная
  и весьма информативная статься про "пространство эмбеддингов":
  
  "Как устроено пространство, в котором думают языковые модели?"
  Автор: Razant (Anton Razzhigaev)
  https://habr.com/ru/companies/airi/articles/804515/
  1 апр 2024 в 16:38
  
  Разбирать и цитировать ее текст здесь не буду, кому интересно см.:
  "Анизотропия и внутренняя размерность "пространства эмбеддингов".
  
  Разве что. приведу ключевую мысль этого материала:
  
  \\\ Ещё раз - у всех LLM во время обучения присутствуют две фазы:
  \\\ инфляция эмбеддингов и их последующая компрессия.
  
  А вот фрагмент дискуссии в комментариях приведу полностью
  - уж очень он контрастирует с изложением в предыдущем фрагменте
  "страшилкой переобучения".
  
  \\\ YuriySeverinov
  \\\ 2 часа назад
  
  \\\ Год-два назад встречал статью,
  \\\ выводы которой заключались в том,
  \\\ что если продолжать учить модель длительное время
  \\\ после того, как она переобучилась,
  \\\ эффект переобучения постепенно начинает пропадать.
  \\\ В ней было сделано предположение,
  \\\ что модель начинает со временем выучивать не конкретные наблюдения,
  \\\ а реальные взаимосвязи и растет ее обобщающая способность.
  
  \\\ Может этот эффект как раз и был связан с тем,
  \\\ что сперва модель формирует большое количество размерностей,
  \\\ а потом начинает их снижать?
  
  \\\ Razant
  \\\ 2 часа назад
  
  \\\ Вы говорите про явление Grokking.
  \\\ https://arxiv.org/pdf/2201.02177.pdf
  //// Гроккинг: обобщение, выходящее за рамки переобучения,
  //// на небольших алгоритмических наборах данных
  //// Алетея Пауэр, Юрий Бурда, Харри Эдвардс, Игорь Бабушкин, Ведант Мисра
  
  \\\ Да, похоже, что это явление и компрессия репрезентаций очень плотно связаны.
  \\\ Схожая интуиция была в статье про OmniGrok.
  \\\ https://arxiv.org/pdf/2210.01117.pdf
  //// Omnigrok: Грокинг за пределами алгоритмических данных
  //// Цимин Лю, Эрик Дж . Мишо, Макс Тегмарк
  
  \\\ Было бы очень интересно посмотреть что происходит с внутренней размерностью
  \\\ при переходе от оверфита к генерализации,
  \\\ предположу что там будет "ступенька"
  \\\ на графике внутренней размерности.
  
  Меня очень заинтересовали эти "Grokking" и "OmniGrok",
  но моих познаний в "замежной мове" явно недостаточно,
  чтобы сделать из указанных ссылок более-менее точные цитаты.
  А машинный перевод тоже получился весьма корявым и неоднозначным.
  
  Но общий смысл действительно такой, как обсуждался в комментариях,
  такие явления действительно можно обнаружить и даже как-то ими управлять.
  Разве что, авторы этих статей исследовали
  достаточно ограниченный класс задач,
  и делать крупные обобщения все-таки остерегаются.
  
  Но, на мой мой взгляд, в этом случае важен сам факт, что такое решение
  - преодоление состояния "переобучения" дальнейшим обучением системы -
  в принципе возможно.
  Если вернуться к "металлургической метафоре" обучения нейросети,
  то такой процесс можно сравнить с "финишной полировкой",
  обязательной, но долгой и нудной,
  если не использовать соответствующий инструмент.
  
  Осталось дело "за малым" - научиться этим пользоваться,
  и подобрать подходящий инструмент.
  
  
  =========
  
  09.04.2024 20:45
  
  Каноническая трактовка регуляризации.
  В оглавление.
  
  эффект приемов регуляризации при обучении нейросетей интересен тем,
  что при очевидном положительном эффекте, подтвержденном значительной практикой,
  он имеет весьма "туманные" теоретические обоснования.
  Нельзя сказать, что их совсем нет, но то, что они явно не достаточны
  для ясного понимания механизма работы этих приемов.
  И, соответственно, отсутствуют какие-то четкие ориентиры
  в каком-то обоснованном выборе из достаточно большого количества этих приемов.
  
  Поэтому в следующем "погружении" в глубокое обучение
  попробуем сделать попытку, если и не окончательно разобраться
  с этой весьма интересной темой, то хотя бы наметить контуры возможного решения:
  
  "Нейросети и глубокое обучение, глава 3, ч.2:
  почему регуляризация помогает уменьшать переобучение?"
  Автор: SLY_G (Вячеслав Голованов)
  https://habr.com/ru/articles/459816/.
  15 июл 2019 в 10:00
  
  Автор оригинала: Michael Nielsen
  http://neuralnetworksanddeeplearning.com/chap3.html#why_does_regularization_help_reduce_overfitting
  
  //// Начало цитирования.
  
  Эмпирически мы увидели, что регуляризация помогает уменьшать переобучение.
  //// "Эмпирически мы увидели" незначительное повышение качества,
  //// а вот имеет это повышение отношение к "переобучению" совсем не ясно.
  //// Нет ни точного определения "переобучения", ни явных способов его измерения.
  //// То, что модель на обучающих данных показывает результаты,
  //// отличающиеся от результатов на тестовых данных,
  //// куда вероятнее, определяется тем, что "наши ожидания",
  //// что нейросеть обладает именно такой формой "обобщения",
  //// которая бы соответствовала бы тестовым данным,
  //// мягко говоря, мало чем обоснованы.
  Это вдохновляет - но, к сожалению,
  не очевидно, почему регуляризация помогает.
  //// Именно потому, что "регуляризация" рассматривается,
  //// как способ "борьбы с переобучением".
  //// А вот если рассматривать это просто как один из способов "повышения качества",
  //// то, может быть, какое-то объяснение и найдется.
  Обычно люди объясняют это как-то так:
  в каком-то смысле, менее крупные веса имеют меньшую сложность,
  что обеспечивает более простое и действенное объяснение данных,
  поэтому им надо отдавать предпочтение.
  Однако это слишком краткое объяснение,
  а некоторые его части могут показаться сомнительными или загадочными.
  Давайте-ка развернём эту историю и изучим её критическим взглядом.
  Для этого предположим, что у нас есть простой набор данных,
  для которого мы хотим создать модель:
  
  .....
  
  По смыслу, здесь мы изучаем явление реального мира,
  и x и y обозначают реальные данные.
  Наша цель - построить модель, позволяющую нам предсказывать y как функцию x.
  Мы могли бы попробовать использовать нейросеть для создания такой модели,
  но я предлагаю нечто более простое:
  я попробую моделировать y как многочлен от x.
  Я буду делать это вместо нейросетей,
  поскольку использование многочленов делает объяснение особенно понятным.
  Как только мы разберёмся со случаем многочлена, мы перейдём к НС.
  На графике выше имеется десять точек,
  что означает, что мы можем найти
  уникальный многочлен 9-го порядка y = a0x9+a1x8+...+a9,
  абсолютно точно укладывающийся в данные.
  И вот график этого многочлена.
  
  ......
  
  Идеальное попадание.
  Но мы можем получить неплохое приближение,
  используя линейную модель y = 2x
  
  ......
  
  Какая из них лучше?
  Какая с большей вероятностью окажется истинной?
  Какая будет лучше обобщаться на другие примеры того же явления реального мира?
  //// А если расширить круг моделей: а как насчет многочлена 10 порядка?
  //// Или одиннадцатого или стоодиннадцатого порядка?
  //// Разве нельзя подобрать соответствующие коэффициенты к таким многочленам,
  //// так чтобы они пусть не точно, хотя, наверняка, можно и точно,
  //// но достаточно близко описывали приведенную зависимость данных?
  //// Тут главный вопрос: а сколько таких многочленов может быть?
  //// Мне кажется что их будет даже не миллионы, а "триллиарды в степени N".
  //// "Нет, N мало - бери M". Короче, бесконечное количество вариантов.
  //// И вот это "бесконечное количество" стоит запомнить.
  //// Чуть дальше мы к нему вернемся.
  
  Сложные вопросы.
  И на них нельзя получить точные ответы,
  не имея дополнительной информации по поводу лежащего в основе данных
  явления реального мира.
  Однако давайте рассмотрим две возможности:
  (1) модель с многочленом 9-го порядка истинно описывает явление реального мира,
  и поэтому, обобщается идеально;
  (2) правильная модель - это y=2x,
  но у нас имеется дополнительный шум,
  //// А если это не "шум", а "гармоническая осцилляция" или что-то еще более хитрое?
  связанный с погрешностью измерений,
  поэтому модель подходит не идеально.
  
  Априори нельзя сказать,
  какая из двух возможностей правильная
  (или что не существует некоей третьей).
  //// И даже не "третьей", а "бесконечного множества" различных возможностей.
  Логически, любая из них может оказаться верной.
  И различие между ними нетривиально.
  Да, на основе имеющихся данных можно сказать,
  что между моделями имеется лишь небольшое отличие.
  Но допустим, мы хотим предсказать значение y,
  соответствующее какому-то большому значению x,
  гораздо большему, чем любое из показанных на графике.
  Если мы попытаемся это сделать,
  тогда между предсказаниями двух моделей появится огромная разница,
  поскольку в многочлене 9-го порядка доминирует член x9,
  а линейная модель линейной и остаётся.
  
  Одна точка зрения на происходящее
  - заявить, что в науке нужно использовать более простое объяснение,
  если это возможно.
  Когда мы находим простую модель, объясняющую многие опорные точки,
  нам так и хочется закричать: <Эврика!>.
  Ведь маловероятно, что простое объяснение появится чисто случайно.
  Мы подозреваем, что модель должна выдавать некую связанную с явлением правду.
  В данном случае модель y=2x+шум кажется гораздо более простой,
  чем y = a0x9+a1x8+...
  Было бы удивительно, если бы простота возникла случайно,
  поэтому мы подозреваем, что y = 2x+шум выражает некую лежащую в основе истину.
  С этой точки зрения модель 9-го порядка просто изучает влияние местного шума.
  И хотя модель 9-го порядка идеально работает
  для данных конкретных опорных точек,
  она не сможет обобщиться на другие точки,
  в результате чего у линейной модели с шумом
  предсказательные возможности будут лучше.
  //// А как насчет промежуточных точек?
  //// Теми самыми из тестового набора?
  //// И откуда взялось убеждение, что решение в виде "простая модель + шум",
  //// лучше "сложная модель + много меньший шум"?
  //// В технике той же радиосвязи уже давно от такого ушли,
  //// в сторону все более и более сложных алгоритмов/моделей/схем.
  
  Давайте посмотрим,
  что означает эта точка зрения для нейросетей.
  Допустим, в нашей сети в основном имеются малые веса,
  как обычно бывает в регуляризированных сетях.
  Благодаря небольшим весам поведение сети не меняется сильно
  при изменении нескольких случайных входов там и сям.
  В итоге регуляризированной сети сложно выучить эффекты местного шума,
  присутствующие в данных.
  //// А если это не "шум"?
  Это похоже на стремление к тому,
  чтобы отдельные свидетельства не сильно влияли на выход сети в целом.
  //// И в итоге даже "три девятки" для большинства нейросетей является
  //// недостижимым рубежом. А нам требуется МИНИМУМ "шесть девяток".
  Регуляризированная сеть вместо этого обучается реагировать
  на такие свидетельства,
  которые часто встречаются в обучающих данных.
  И наоборот, сеть с крупными весами
  может довольно сильно менять своё поведение
  в ответ на небольшие изменения входных данных.
  Поэтому нерегуляризированная сеть может использовать большие веса
  для обучения сложной модели,
  содержащей много информации о шуме в обучающих данных.
  Короче говоря, ограничения регуляризированных сетей
  позволяют им создавать относительно простые модели на основе закономерностей,
  часто встречающихся в обучающих данных,
  и они устойчивы к отклонениям, вызванным шумом в обучающих данных.
  Есть надежда, что это заставит наши сети изучать именно само явление,
  и лучше обобщать полученные знания.
  //// Опять эта мантра про "обощение" И откровенно неубедительное объяснение
  //// принципа действия "эффекта регуляризации".
  //// Свой вариант объяснения этого эффекта постараюсь изложить чуть ниже,
  //// там это будет более к месту.
  
  Учитывая всё сказанное, идея того,
  чтобы отдавать предпочтения более простым объяснениям,
  должна заставить вас нервничать.
  Иногда люди называют эту идею <бритвой Оккама> и рьяно применяют её,
  будто бы она обладает статусом общего научного принципа.
  Но это, разумеется, не общий научный принцип.
  Нет никакой априорной логической причины
  предпочитать простые объяснения сложным.
  Иногда более сложное объяснение оказывается правильным.
  
  Позвольте описать два примера того,
  как более сложное объяснение оказалось правильным.
  В 1940-х физик Марсель Шейн объявил об открытии новой частицы.
  Компания, на которую он работал, General Electric, была в восторге,
  и широко распространила публикацию об этом событии.
  Однако физик Ханс Бете отнёсся к нему скептически.
  Бете посетил Шейна и изучил пластинки со следами новой частицы Шейна.
  Шейн показывал Бете пластинку за пластинкой,
  но на каждой из них Бете находил какую-либо проблему,
  говорившую о необходимости отказа от этих данных.
  Наконец, Шейн показал Бете пластинку, выглядевшую годно.
  Бете сказал, что это, возможно, просто статистическое отклонение.
  Шейн: <Да, но шансы на то, что это из-за статистики,
  даже по вашей собственной формуле, один к пяти>.
  Бете: <Однако я уже посмотрел на пять пластинок>.
  Наконец, Шейн сказал:
  <Но каждую мою пластинку, каждое хорошее изображение вы объясняли
  какой-то другой теорией,
  а у меня есть одна гипотеза, объясняющая все пластинки сразу,
  из которой следует, что речь идёт о новой частице>.
  Бете ответил:
  <Единственное отличие между моими и вашими объяснениями в том,
  что ваши неправильные, а мои правильные.
  Ваше единое объяснение неверно, а все мои объяснения верны>.
  Впоследствии выяснилось, что природа согласилась с Бете,
  и частица Шейна испарилась.
  //// Как просто было когда-то "открывать" и "закрывать" новые частицы.
  //// Примерно как сейчас с новыми "практиками" машинного обучения.
  
  Во втором примере, в 1859 году астроном Урбен Жан Жозеф Леверье обнаружил,
  что форма орбиты Меркурия не соответствует теории всемирного тяготения Ньютона.
  Существовало крохотное отклонение от этой теории,
  и тогда было предложено несколько вариантов решения проблемы,
  которые сводились к тому, что теория Ньютона в целом верна,
  и требует лишь небольшого изменения.
  А в 1916 году Эйнштейн показал, что это отклонение можно хорошо объяснить
  с использованием его общей теории относительности,
  радикально отличающейся от ньютоновской гравитации
  и основанной на куда как более сложной математике.
  Несмотря на эту дополнительную сложность,
  сегодня принято считать, что объяснение Эйнштейна верно,
  а Ньютоновская гравитация неверна даже в модифицированной форме.
  Так получается, в частности, потому, что сегодня нам известно,
  что теория Эйнштейна объясняет многие другие явления,
  с которыми у теории Ньютона были сложности.
  Более того, что ещё более поразительно,
  теория Эйнштейна точно предсказывает несколько явлений,
  которых Ньютоновская гравитация не предсказывала вообще.
  Однако эти впечатляющие качества не были очевидными в прошлом.
  Если судить исходя из одной лишь простоты,
  то некоторая модифицированная форма Ньютоновской теории
  выглядела бы привлекательнее.
  //// А куда отнести всю фактологию, которую относят к "переобучению"?
  //// Так будем считать, что нейросети "переобучаются",
  //// т.е. якобы точно выучивают обучающую выборку,
  //// и поэтому показывают худшие результаты на тестовой?
  //// А то, что они и обучающую выборку с точностью "пять девяток"
  //// осилить не в состоянии - это куда отнести?
  //// А что стоит за такими явлениями как "Grokking" и "Omnigrok",
  //// которые упоминались в предыдущем фрагменте?
  //// Какие в этом случае "механика" и "относительность"?
  
  Из этих историй можно извлечь три морали.
  Во-первых, иногда довольно сложно решить,
  какое из двух объяснений будет <проще>.
  Во-вторых, даже если бы мы и приняли такое решение,
  простотой нужно руководствоваться крайне осторожно!
  В-третьих, истинной проверкой модели является не простота,
  а то, насколько хорошо она предсказывает новые явления
  в новых условиях поведения.
  
  Учитывая всё это и проявляя осторожность,
  примем эмпирический факт
  - регуляризированные НС обычно обобщаются лучше, чем нерегуляризированные.
  Поэтому далее в книге мы будем часто использовать регуляризацию.
  Упомянутые истории нужны лишь для того, чтобы объяснить,
  почему никто пока ещё не разработал
  полностью убедительное теоретическое объяснение тому,
  почему регуляризация помогает сетям проводить обобщение.
  //// Потому что нейросети НЕ ОБОБЩАЮТ,
  //// а подгоняют "рельеф решения" под "ландшафт задачи",
  //// который во многом зависит и от функции ошибки и регуляризационных алгоритмов.
  //// И пока объяснения пытаются выстроить от этого "обобщения",
  //// внятного объяснения вряд ли можно ожидать скоро.
  Исследователи продолжают публиковать работы,
  где пытаются испробовать различные подходы к регуляризации,
  сравнивать их, смотря, что лучше работает,
  и пытаясь понять, почему различные подходы работают хуже или лучше.
  Так что к регуляризации можно относиться, как к клуджу.
  \\\ техн., жарг. клудж - устройство, программа или часть программы,
  \\\ которые теоретически не должны работать,
  \\\ но почему-то работают
  Когда она, довольно часто, помогает,
  у нас нет полностью удовлетворительного системного понимания происходящего
  - только неполные эвристические и практические правила.
  
  Здесь скрывается и более глубокий набор проблем,
  идущих к самому сердцу науки.
  Это вопрос обобщения.
  Регуляризация может дать нам вычислительную волшебную палочку,
  помогающую нашим сетям лучше обобщать данные,
  но не даёт принципиального понимания того,
  как работает обобщение,
  и какой лучший подход к нему.
  
  Эти проблемы восходят к проблеме индукции,
  известное осмысление которой проводил шотландский философ Дэвид Юм
  в книге "Исследование о человеческом познании" (1748).
  Проблеме индукции посвящена "теорема об отсутствии бесплатных обедов"
  Дэвида Уолперта и Уильяма Макреди (1977).
  
  А это особенно досадно,
  поскольку в обычной жизни люди феноменально хорошо умеют обобщать данные.
  Покажите несколько изображений слона ребёнку,
  и он быстро научится распознавать других слонов.
  Конечно, он иногда может ошибиться,
  к примеру, перепутать носорога со слоном,
  но в целом этот процесс работает удивительно точно.
  Вот, у нас есть система - мозг человека -
  с огромным количеством свободных параметров.
  И после того, как ему показывают одно или несколько обучающих изображений,
  система обучается обобщать их до других изображений.
  Наш мозг, в каком-то смысле,
  удивительно хорошо умеет регуляризировать!
  Но как мы это делаем?
  На данный момент нам это неизвестно.
  Думаю, что в будущем мы выработаем
  боле мощные технологии регуляризации в искусственных нейросетях,
  техники, которые в итоге позволят НС обобщать данные,
  исходя из ещё менее крупных наборов данных.
  //// Еще одна иллюстрация, когда зациклившись на проблеме "обобщения",
  //// пытаются представить работу алгоритма обучения нейросети
  //// по аналогии с устройством совершенно иного принципа действия
  //// - в мозгу нет обратного распространения ошибки -
  //// и закономерно не получают в итоге ничего кроме общих фраз.
  
  На самом деле, наши сети и так уже обобщают куда лучше,
  чем можно было ожидать априори.
  Сеть со 100 скрытыми нейронами обладает почти 80 000 параметров.
  У нас есть только 50 000 изображений в обучающих данных.
  Это всё равно, как пытаться натянуть многочлен 80 000 порядка
  на 50 000 опорных точек.
  По всем признакам наша сеть должна ужасно переобучиться.
  И всё же, как мы видели, такая сеть на самом деле довольно неплохо обобщает.
  Почему так происходит?
  Это не совсем понятно.
  Была высказана гипотеза,
  что <динамика обучения градиентным спуском в многослойных сетях
  подвержена саморегуляризации>.
  Это чрезвычайная удача,
  но и довольно тревожный факт,
  поскольку мы не понимаем, почему так происходит.
  Тем временем мы примем прагматичный подход,
  и будем использовать регуляризацию везде, где только можно.
  Нашим НС это будет на пользу.
  //// Вот так "нейронаука" на факту превращается в "шаманскую практику".
  
  Позвольте мне закончить этот раздел, вернувшись к тому,
  что я раньше не объяснил:
  что регуляризация L2 не ограничивает смещения.
  Естественно, было бы легко изменить процедуру регуляризации,
  чтобы она регуляризировала смещения.
  Но эмпирически это часто не меняет результаты каким-то заметным образом,
  поэтому, до некоторой степени, заниматься регуляризацией смещений, или нет
  - вопрос соглашения.
  Однако стоит отметить, что крупное смещение
  не делает нейрон чувствительным ко входам так, как крупные веса.
  Поэтому нам не нужно беспокоиться по поводу крупных смещений,
  позволяющим нашим сетям обучаться шуму в обучающих данных.
  В то же время, разрешив большие смещения,
  мы делаем наши сети более гибкими в их поведении
  - в частности, крупные смещения облегчают насыщение нейронов,
  чего нам бы хотелось.
  По этой причине обычно мы не включаем смещения в регуляризацию.
  //// Надо будет этот вопрос проработать с Copilot.
  //// Глядишь, что-то интересное удастся выцепить,
  //// какой-то не очень обоснованный вывод в этом параграфе.
  
  Иные техники регуляризации
  
  Существует множество техник регуляризации, кроме L2.
  На самом деле, было разработано уже столько техник,
  что я бы при всём желании не смог кратко описать их все.
  В этом разделе я кратенько опишу три других подхода к уменьшению переобучения:
  регуляризацию L1,
  исключение [dropout]
  и искусственное увеличение обучающего набора.
  Мы не будем изучать их так глубоко, как предыдущие темы.
  Вместо этого мы просто познакомимся с ними,
  а заодно оценим разнообразие существующих техник регуляризации.
  
  Регуляризация L1
  
  В данном подходе мы изменяем нерегуляризованную функцию стоимости,
  добавляя сумму абсолютных значений весов:
  
  $ C = C_0 + \frac{\lambda}{n} \sum_w |w| \tag{95} $
  
  Интуитивно это похоже на регуляризацию L2,
  штрафующую за большие веса и заставляющую сеть предпочитать малые веса.
  Конечно, член регуляризации L1 не похож на член регуляризации L2,
  поэтому не стоит ожидать ровно такого же поведения.
  Давайте попробуем понять, в чём поведение сети,
  обученной при помощи регуляризации L1,
  отличается от сети, обученной при помощи регуляризации L2.
  
  Для этого посмотрим на частные производные функции стоимости.
  Дифференцируя (95), получаем:
  
  $ \frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w} + \frac{\lambda}{n} \, {\rm sgn}(w) \tag{96} $
  
  где sgn(w) - знак w,
  то есть, +1, если w положительная,
  и -1, если w отрицательная.
  При помощи этого выражения мы влёгкую модифицируем обратное распространение
  так, чтобы оно выполняло стохастический градиентный спуск
  при помощи регуляризации L1.
  Итоговое правило обновление для L1-регуляризованной сети:
  
  $ w \rightarrow w' = w-\frac{\eta \lambda}{n} \mbox{sgn}(w) - \eta \frac{\partial C_0}{\partial w} \tag{97} $
  
  где, как обычно, ?C/?w можно по желанию оценить
  через усреднённое значение мини-пакета.
  Сравним это с правилом обновления регуляризации L2 (93):
  
  $ w \rightarrow w' = w\left(1 - \frac{\eta \lambda}{n} \right) - \eta \frac{\partial C_0}{\partial w} \tag{98} $
  
  В обоих выражениях действие регуляризации
  заключается в уменьшении весов.
  Это совпадает с интуитивным представлением о том,
  что оба типа регуляризации штрафуют большие веса.
  Однако уменьшаются веса по-разному.
  В регуляризации L1 веса уменьшаются на постоянное значение, стремясь к 0.
  В регуляризации L2 веса уменьшаются на значение, пропорциональное w.
  Поэтому когда у какого-то веса оказывается большое значение |w|,
  регуляризация L1 уменьшает вес не так сильно, как L2.
  И наоборот, когда |w| мало, регуляризация L1 уменьшает вес гораздо больше,
  чем регуляризация L2.
  В итоге регуляризация L1 стремится сконцентрировать веса сети
  в относительно небольшом числе связей высокой важности,
  а другие веса стремятся к нулю.
  //// Эдакое мягкое прореживание связей.
  //// И самое интересное, почему это работает?
  
  Я слегка сгладил одну проблему в предыдущем обсуждении
  - частная производная ?C/?w не определена, когда w=0.
  Это потому, что у функции |w| имеется острый <излом> в точке w=0,
  поэтому там её дифференцировать нельзя.
  Но это не страшно.
  Мы просто применим обычное, нерегуляризированное правило
  для стохастического градиентного спуска, когда w=0.
  Интуитивно, в этом нет ничего плохого
  - регуляризация должна уменьшать веса,
  и, очевидно, она не может уменьшить вес, уже равный 0.
  Точнее говоря, мы будем использовать уравнения (96) и (97) с условием,
  что sgn(0)=0.
  Это даст нам удобное и компактное правило для стохастического градиентного спуска
  с регуляризацией L1.
  
  Исключение [dropout]
  
  Исключение - совершенно другая техника регуляризации.
  В отличие от регуляризации L1 и L2,
  исключение не занимается изменением функции стоимости.
  Вместо этого мы изменяем саму сеть.
  Давайте я объясню базовую механику работы исключения,
  перед тем, как углубляться в тему того, почему оно работает
  и с какими результатами.
  
  Допустим, мы пытаемся обучить сеть:
  
  В частности, допустим, у нас есть обучающие входные данные x
  и соответствующий желаемый выход y.
  Обычно мы бы обучали её прямым распространением x по сети,
  а потом обратным распространением, чтобы определить вклад градиента.
  Исключение изменяет этот процесс.
  Мы начинаем со случайного и временного удаления половины скрытых нейронов сети,
  оставляя без изменений входные и выходные нейроны.
  После этого у нас останется примерно такая сеть.
  Отметьте, что исключённые нейроны, те, что временно удалены,
  всё равно отмечены на схеме:
  
  ......
  
  Мы передаём x прямым распространением по изменённой сети,
  а потом обратно распространяем результат, тоже по изменённой сети.
  После того, как мы проделаем это с мини-пакетом примеров,
  мы обновляем соответствующие веса и смещения.
  Потом мы повторяем этот процесс,
  сначала восстанавливая исключённые нейроны,
  потом выбирая новое случайное подмножество скрытых нейронов для удаления,
  оцениваем градиент для другого мини-пакета, и обновляем веса и смещения сети.
  
  Повторяя этот процесс снова и снова,
  мы получим сеть, выучившую некие веса и смещения.
  Естественно, эти веса и смещения выучивались в условиях,
  при которых половина скрытых нейронов была исключена.
  И когда мы запускаем сеть по полной,
  у нас будет в два раза больше активных скрытых нейронов.
  Для компенсации этого мы ополовиниваем веса, исходящие от скрытых нейронов.
  
  Процедура исключения может показаться странной и произвольной.
  Почему она должна помочь с регуляризацией?
  Чтобы объяснить происходящее,
  я хочу, чтобы вы на время забыли про исключение,
  и представили обучение НС стандартным способом.
  В частности, представьте, что мы обучаем несколько разных НС,
  используя одинаковые обучающие данные.
  Конечно, сети сначала могут различаться,
  и иногда обучение может давать разные результаты.
  В таких случаях мы могли бы применить какое-либо усреднение
  или схему голосования, чтобы решить, какой из выходов принимать.
  К примеру, если мы обучили пять сетей,
  и три из них классифицируют цифру, как <3>,
  тогда, вероятно, это и правда тройка.
  А две других сети, вероятно, просто ошибаются.
  Такая схема усреднения часто оказывается полезным
  (пусть и дорогим) способом уменьшения переобучения.
  Причина в том, что разные сети могут переобучаться по-разному,
  и усреднение может помочь с устранением подобного переобучения.
  //// Очень хорошая аналогия, но ее стоило бы еще "чуть-чуть" развить.
  //// А что если действительно при обучении нейросети происходит
  //// КОНКУРИРУЮЩЕЕ обучение МНОЖЕСТВА подсетей
  //// даже если не используется регуляризация?
  //// И мифическая "обобщающая способность" нейросети
  //// просто проявление СУММЫ реакций огромного количества подсетей.
  
  //// Возьмите эту мысль на обдумывания, не отвергая ее "с порога".
  //// Чуть ниже попробую показать, что не настолько уж "завиральная".
  
  Как всё это связано с исключением? Эвристически,
  когда мы исключаем разные наборы нейтронов,
  это похоже на то, как если бы мы обучали разные НС.
  Поэтому процедура исключения похожа на усреднение эффектов
  по очень большому количеству разных сетей.
  Разные сети переобучаются по-разному,
  //// И "недообучаются" то же "по разному",
  //// и сама процедура обучения у каждой подсети идет чуть-чуть по разному.
  поэтому есть надежда,
  что средний эффект исключения уменьшит переобучение.
  //// Как там насчет когнитивистской эвристики о "мудрости толпы"?
  \\\ Мудрость толпы (англ. Wisdom of the crowd)
  \\\ - коллективное мнение разнообразной независимой группы лиц,
  \\\ а не одного эксперта.
  \\\ Этот процесс, хотя и не является новым для информационной эпохи,
  \\\ был выдвинут в центр внимания социальных информационных сайтов,
  \\\ таких как Quora, Reddit, Stack Exchange, Wikipedia, Yahoo! Answers
  \\\ и других веб-ресурсов,
  \\\ которые полагаются на коллективные человеческие знания[1].
  \\\ Объяснение этого явления заключается в том,
  \\\ что существует своеобразный шум, связанный с каждым отдельным суждением,
  \\\ и взятие среднего значения по большому количеству ответов
  \\\ каким-то образом поможет подавить эффект этого шума[2].
  \\\ https://cyclowiki.org/wiki/Мудрость_толпы
  
  Связанное с этим эвристическое объяснение пользы исключения
  даётся в одной из самых ранних работ, использовавших эту технику:
  https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf
  <Эта техника уменьшает сложную совместную адаптацию нейронов,
  поскольку нейрон не может полагаться на присутствие определённых соседей.
  В итоге ему приходиться обучаться более надёжным признакам,
  которые могут быть полезными в совместной работе
  со многими различными случайными подмножествами нейронов>.
  Иначе говоря, если представить нашу НС, как модель, делающую предсказания,
  то исключение будет способом гарантировать устойчивость модели
  к потерям отдельных частей свидетельств.
  В этом смысле техника напоминает регуляризации L1 и L2,
  стремящиеся уменьшать веса,
  и делающие таким способом сеть более устойчивой
  к потерям любых отдельных связей в сети.
  //// Вот опять же попытка представить обучающие примеры,
  //// как "нечто с шумом".
  //// Но ведь если данные предельно чистые,
  //// но просто с очень сложной зависимостью,
  //// разве подход к ним как к "чему-то с шумом"
  //// уже гарантированно НЕ позволит на тестовой выборке получить
  //// "заветные шесть девяток"?
  
  Естественно, истинная мера полезности исключения
  - её огромные успехи в улучшении эффективности нейросетей.
  В оригинальной работе, где был представлен этот метод,
  http://arxiv.org/pdf/1207.0580.pdf
  он применялся ко множеству разных задач.
  Нас особенно интересует то,
  что авторы применили исключение к классификации цифр из MNIST,
  используя простейшую сеть с прямым распространением,
  похожую на ту, что рассматривали мы.
  В работе отмечается, что до тех пор наилучшим результатом
  для подобной архитектуры была точность в 98,4%.
  Они улучшили её до 98,7%,
  используя комбинацию исключения и изменённой формы регуляризации L2.
  Настолько же впечатляющие результаты были получены
  и для многих других задач, включая распознавание образов и речи,
  и обработку естественного языка.
  Исключение было особенно полезным в обучении крупных глубоких сетей,
  где часто остро встаёт проблема переобучения.
  //// Вот опять же "логическая нестыковка" - чем более крупная модель,
  //// тем больше у нее свободных параметров,
  //// так почему же она "предпочитает переобучаться",
  //// а не "обобщать" все более и более сложные зависимости.
  //// Даже странно, что такое несоответствие в даже на одной странице
  //// "почему-то" старательно игнорируется в большинстве учебных материалов.
  //// А может быть и вообще во всех проработанных мной.
  //// Вот не могу вспомнить ни одного примера упоминания этой "нестыковки",
  //// не говоря уже о попытках ее как-то раскрыть/объяснить.
  
  //// А вот если представлять нейросеть как "толпу" подсетей
  //// самых разных размеров,
  //// то тогда среди них, наверняка, найдется даже не одна, а много,
  //// которые могут страдать от "классического переобучения".
  //// Хотя я считаю, что и это "не совсем так",
  //// но, может быть, этот аргумент облегчит восприятие любой нейросети
  //// как "сети сетей", точнее, "ансамблей", а не отдельных нейронов.
  
  Искусственное расширение набора обучающих данных
  
  Ранее мы видели, что наша точность классификации MNIST
  упала до 80 с чем-то процентов,
  когда мы использовали всего 1000 обучающих изображений.
  И неудивительно - с меньшим количеством данных наша сеть
  встретит меньше вариантов написания цифр людьми.
  Давайте попробуем обучить нашу сеть из 30 скрытых нейронов,
  используя разные объёмы обучающего набора,
  чтобы посмотреть на изменение эффективности.
  Мы обучаем, используя размер мини-пакета в 10, скорость обучения ? = 0,5,
  параметр регуляризации ?=5,0,
  и функцию стоимости с перекрёстной энтропией.
  Мы будем обучать сеть 30 эпох с использованием полного набора данных,
  и увеличивать количество эпох пропорционально уменьшению объёма обучающих данных.
  Чтобы гарантировать одинаковый фактор уменьшения весов
  для разных наборов обучающих данных,
  мы будем использовать параметр регуляризации ?=5,0 с полным обучающим набором,
  и пропорционально уменьшать его с уменьшением объёмов данных.
  
  ......
  
  Видно, что точность классификации значительно подрастает
  с увеличением объёмов обучающих данных.
  Вероятно, этот рост будет продолжаться с дальнейшим увеличением объёмов.
  Конечно, судя по графику выше, мы приближаемся к насыщению.
  Однако, допустим, что мы переделаем этот график
  на логарифмическую зависимость от объёма обучающих данных:
  
  ......
  
  Видно, что в конце график всё равно стремится вверх.
  Это говорит о том, что если мы возьмём гораздо более массивный объём данных
  - допустим, миллионы или даже миллиарды рукописных примеров, а не 50 000 -
  тогда мы, вероятно, получим гораздо лучше работающую сеть
  даже такого небольшого размера.
  //// А как же "опасность переобучения"?
  //// Или большие объемы данных ГАРАНТИРОВАННО устраняют эту проблему?
  
  Достать больше обучающих данных - прекрасная идея.
  К сожалению, это может обойтись дорого,
  поэтому на практике не всегда возможно.
  Однако есть и другая идея, способная сработать почти так же хорошо
  - искусственно увеличить набор данных.
  К примеру, допустим, мы возьмём изображение пятёрки из MNIST,
  и немного повернём его, градусов на 15:
  
  .......
  
  Это явно та же цифра.
  Но на пиксельном уровне она сильно отличается от изображений,
  имеющихся в базе MNIST.
  Разумно предположить, что добавление этого изображения
  к обучающему набору данных может помочь нашей сети узнать больше
  о классификации изображений.
  Более того, мы, очевидно, не ограничены возможностью добавления
  всего одного изображения.
  Мы можем расширить наши обучающие данные,
  сделав несколько небольших поворотов всех обучающих картинок из MNIST,
  а потом использовав расширенный набор обучающих данных
  для увеличения эффективности сети.
  
  Эта идея весьма мощная, и её широко используют.
  Посмотрим на результаты из научной работы,
  применившей несколько вариаций этой идеи к MNIST.
  Одна из архитектур рассматриваемых ими сетей была похожа на ту,
  что используем мы
  - сеть с прямым распространением с 800 скрытыми нейронами,
  использующую функцию стоимости с перекрёстной энтропией.
  Запустив эту сеть со стандартным обучающим набором MNIST,
  они получили точность классификации в 98,4%.
  Но затем они расширили обучающие данные,
  используя не только описанною мною выше вращение,
  но и перенос и искажение изображений.
  Обучив сеть на расширенных данных, они повысили её точность до 98,9%.
  Также они экспериментировали с т.н. <эластичными искажениями>,
  особым типом искажений изображения,
  призванным устранить случайные колебания мускулов руки.
  Используя эластичные искажения для расширения данных,
  они достигли точности в 99,3%.
  По сути, они расширяли опыт их сети,
  выдавая ей различные вариации рукописного текста,
  встречающиеся в реальных почерках.
  
  Варианты этой идеи можно использовать для улучшения показателей
  множества задач по обучению,
  не только для распознавания почерка.
  Общий принцип - расширить обучающие данные, применяя к ним операции,
  отражающие вариации, встречающиеся в реальности.
  Такие вариации несложно придумать.
  Допустим, мы создаём НС для распознавания речи.
  Люди могут распознавать речь даже при наличии таких искажений,
  как фоновый шум.
  Поэтому можно расширить данные, добавив фонового шума.
  Также мы способны распознавать ускоренную и замедленную речь.
  Это ещё один способ расширения обучающих данных.
  Эти техники используются не всегда
  - к примеру, вместо расширения обучающего набора через добавления шума,
  может оказаться более эффективным подчищать входные данные,
  применяя к ним фильтр шума.
  И всё же, стоит иметь в виду идею расширения обучающего набора,
  и искать способы её применения.
  
  Упражнение
  
  Как мы обсудили выше, один из способов расширить обучающие данные из MNIST
  - использовать небольшие повороты обучающих картинок.
  Какая проблема может появиться,
  если мы допустим повороты картинок на любые углы?
  //// Я с этим упражнением не справился - в голову ничего путного не пришло.
  //// Видимо, моя встроенная нейросеть слегка "переобучилась".
  
  Отступление, касающееся больших данных
  и о смысле сравнения точности классификаций
  
  Давайте вновь взглянем на то,
  как точность нашей НС изменяется в зависимости от размера обучающего набора:
  
  Допустим, что вместо использования НС мы бы использовали
  другую технологию машинного обучения для классификации цифр.
  К примеру, попробуем использовать метод опорных векторов
  (support vector machine, SVM),
  с которым мы кратко встречались в главе 1.
  Как и тогда, не волнуйтесь, если вы не знакомы с SVM,
  нам не надо разбираться в его деталях.
  Мы будем использовать SVM за счёт библиотеки scikit-learn.
  Вот, как меняется эффективность SVM в зависимости от размера обучающего набора.
  Для сравнения я нанёс на график и результаты работы НС.
  
  .......
  
  Вероятно, первое, что бросается в глаза
  - НС превосходит SVM на любом размере обучающего набора.
  Это хорошо, хотя не стоит делать из этого далеко идущих выводов,
  поскольку я использовал предустановленные настройки scikit-learn,
  а над нашей НС мы довольно серьёзно поработали.
  Менее яркий, но более интересный факт, следующий из графика, состоит в том,
  что если мы обучим наш SVM с использованием 50 000 изображений,
  то он сработает лучше (точность в 94,48%), чем наша НС,
  обученная на 5000 изображений (93,24%).
  Иначе говоря, увеличение объёма обучающих данных
  иногда компенсирует разницу в алгоритмах МО.
  
  Может произойти ещё нечто более интересное.
  Допустим, мы пытаемся решить задачу с использованием двух алгоритмов МО, A и B.
  Иногда бывает так, что алгоритм A опережает алгоритм B
  на одном наборе обучающих данных,
  а алгоритм B опережает алгоритм A на другом наборе обучающих данных.
  Выше мы этого не увидели
  - тогда графики бы пересеклись -
  но такое бывает.
  Правильный ответ на вопрос:
  <Превосходит ли алгоритм A алгоритм B?>
  на самом деле такой:
  <А какой обучающий набор данных вы используете?>
  
  Всё это необходимо учитывать, как во время разработки,
  так и во время чтения научных работ.
  Многие работы концентрируются на поиске новых трюков
  для выжимания лучших результатов на стандартных наборах данных для измерения.
  <Наша супер-пупер технология дала нам улучшение на X%
  на стандартном сравнительном наборе Y>
  - каноническая форма заявления в таком исследовании.
  Иногда подобные заявления на самом деле бывают интересными,
  но стоит понимать,
  что они применимы только в контексте определённого обучающего набора.
  Представьте себе альтернативную историю,
  в которой люди, изначально создавшие сравнительный набор,
  получили исследовательский грант покрупнее.
  Они могли бы использовать дополнительные деньги
  для сбора дополнительных данных.
  Вполне возможно, что <улучшение> супер-пупер технологии исчезло бы
  на большем наборе данных.
  Иначе говоря, суть улучшения может оказаться просто случайностью.
  //// Или, что более вероятно, срабатывающей на определенном этапе обучения.
  //// Снова напомню концепцию многокритериального/многоступенчатого обучения.
  Из этого в область практического применения нужно вынести следующую мораль:
  нам необходимо как улучшение алгоритмов,
  так и улучшение обучающих данных.
  Нет ничего плохого в том, чтобы искать улучшенные алгоритмы,
  но убедитесь, что вы не концентрируетесь на этом,
  игнорируя более лёгкий способ выиграть при помощи увеличения объёма
  или качества обучающих данных.
  
  Задача
  
  Исследовательская задача.
  Как наши алгоритмы МО будут вести себя в пределе
  на очень больших наборах данных?
  Для любого заданного алгоритма естественно попытаться определить
  понятие асимптотической эффективности в пределе
  на самом деле больших данных.
  Дешёвый и сердитый подход к этой задаче
  - попытаться подобрать кривые под графики,
  подобные тем, что приведены выше,
  а потом экстраполировать кривые в бесконечность.
  Однако можно возразить, что разные подходы к подбору кривых
  могут дать разное представление об асимптотическом пределе.
  Сможете ли вы оправдать результаты экстраполяции
  для какого-то определённого класса кривых?
  //// Мне представляется, что скорее "нет" чем "да".
  //// Во всяком случае, мне еще ни разу такие графики/экстраполяции не встречались.
  В таком случае, сравните асимптотические пределы нескольких алгоритмов МО.
  //// Мимо. Нет таких данных.
  
  Итоги
  
  Мы закончили наше погружение в переобучение и регуляризацию.
  Мы, конечно, ещё вернёмся к этим проблемам.
  Как я уже несколько раз упомянул,
  переобучение - большая проблема в области НС,
  особенно по мере того, как компьютеры становятся всё мощнее,
  и мы можем обучать всё более крупные сети.
  В итоге возникает насущная необходимость
  разработать эффективные методики регуляризации
  для уменьшения переобучения,
  поэтому данная область сегодня является весьма активной.
  //// Не регуляризацией единой живет сейчас машинное обучение.
  
  Инициализация весов
  
  Когда мы создаём наши НС, нам необходимо делать выбор
  начальных значений весов и смещений.
  Пока что мы выбирали их согласно предписаниям,
  кратко описанным мною в главе 1.
  Напомню, что мы выбирали веса и смещения на основе
  независимого распределения Гаусса
  с математическим ожиданием 0 и среднеквадратичным отклонением 1.
  Этот подход хорошо сработал,
  однако он кажется довольно произвольным,
  поэтому стоит пересмотреть его и подумать,
  нельзя ли найти лучший способ назначения изначальных весов и смещений,
  и, возможно, помочь нашим НС учиться быстрее.
  
  Оказывается, можно довольно серьёзно улучшить процесс инициализации
  по сравнению с нормализованным распределением Гаусса.
  Чтобы разобраться в этом, допустим,
  мы работаем с сетью с большим количеством входных нейронов,
  - скажем, с 1000.
  И допустим, мы использовали нормализованное распределение Гаусса
  для инициализации весов, соединённых с первым скрытым слоем.
  Пока что я сфокусируюсь только на весах,
  соединяющие входные нейроны с первым нейроном в скрытом слое,
  и проигнорирую остальную часть сети:
  
  Для простоты представим, что мы пытаемся обучать сеть входом x,
  в котором половина входных нейронов включены,
  то есть, имеют значение 1, а половина - выключены,
  то есть, имеют значение 0.
  Следующий аргумент работает и в более общем случае,
  но вам проще будет понять его на этом особом примере.
  Рассмотрим взвешенную сумму z = ?jwjxj+b входов для скрытого нейрона.
  500 членов суммы исчезают, поскольку соответствующие xj равны 0.
  Поэтому z - это сумма 501 нормализованных гауссовых случайных переменных,
  500 весов и 1 дополнительное смещение.
  Поэтому и само значение z имеет гауссово распределение
  с математическим ожиданием 0 и среднеквадратичным отклонением √501 ~ 22,4.
  То есть, у z довольно широкое гауссово распределение, без острых пиков:
  
  ......
  
  В частности, из этого графика видно, что |z|,
  скорее всего, будет довольно крупным,
  то есть, z > 1 или z < -1.
  В таком случае выход скрытых нейронов ?(z) будет очень близок к 1 или 0.
  Это значит, что наш скрытый нейрон насытится.
  И когда это произойдёт, как нам уже известно,
  небольшие изменения весов будут давать крохотные изменения
  в активации скрытого нейрона.
  Эти крохотные изменения, в свою очередь,
  практически не затронут остальные нейтроны в сети,
  и мы увидим соответствующие крохотные изменения в функции стоимости.
  В итоге эти веса будут обучаться очень медленно,
  когда мы используем алгоритм градиентного спуска.
  Это похоже на задачу, которую мы уже обсуждали в этой главе,
  в которой выходные нейроны, насыщенные на неверных значениях,
   заставляют обучение замедляться.
  Раньше мы решали эту проблему, хитроумно выбирая функцию стоимости.
  К сожалению, хотя это помогло с насыщенными выходными нейронами,
  это совсем не помогает с насыщением скрытых нейронов.
  //// Ага, как я и предполагал, для глубинных слоев "логистический нейрон"
  //// - существенный "тормоз" на пути "обратного распространения",
  //// и, похоже, применять такую активацию в глубине нейросети
  //// надо очень обдуманно и осторожно.
  
  Сейчас я говорил о входящих весах первого скрытого слоя.
  Естественно, те же аргументы применимы и к следующим скрытым слоям:
  если веса в поздних скрытых слоях инициализируются
  с использованием нормализованных гауссовых распределений,
  их активации часто будут близки к 0 или 1,
  и обучение будет идти очень медленно.
  
  Есть ли способ выбрать лучшие варианты инициализации для весов и смещений,
  чтобы мы не получали такого насыщения,
  и могли избежать замедления обучения?
  Допустим, у нас будет нейрон с количеством входящих весов nin.
  Тогда нам надо инициализировать эти веса случайными гауссовыми распределениями
  с математическим ожиданием 0 и среднеквадратичным отклонением 1/√nin.
  То есть, мы сжимаем гауссианы,
  и уменьшаем вероятность насыщения нейрона.
  Затем мы выберем гауссово распределение для смещений
  с математическим ожиданием 0 и среднеквадратичным отклонением 1,
  по причинам, к которым я вернусь чуть позже.
  Сделав такой выбор, мы вновь получим, что z = ?jwjxj + b
  будет случайной переменной с гауссовым распределением
  с математическим ожиданием 0,
  однако с гораздо более выраженным пиком, чем раньше.
  Допустим, как и раньше, что 500 входов равны 0, и 500 равны 1.
  Тогда легко показать (см. упражнение ниже),
  что z имеет гауссово распределение с математическим ожиданием 0
  и среднеквадратичным отклонением √(3/2) = 1,22...
  Этот график с гораздо более острым пиком,
  настолько, что даже на картинке ниже ситуация несколько преуменьшена,
  поскольку мне пришлось поменять масштаб вертикальной оси
  по сравнению с предыдущим графиком:
  
  ......
  
  Такой нейрон насытится с гораздо меньшей вероятностью,
  и, соответственно, с меньшей вероятностью столкнётся с замедлением обучения.
  //// Т.е., по сути, большинство инициализируемых весов
  //// имеют существенно меньшее значение, чем в исходном алгоритме.
  
  Упражнение
  
  .....
  
  Выше я упоминал, что мы и дальше будем инициализировать смещения, как и раньше,
  на основе независимого распределения Гаусса
  с математическим ожиданием 0 и среднеквадратичным отклонением 1.
  И это нормально,
  поскольку не сильно увеличивает вероятность насыщения наших нейронов.
  На самом деле инициализация смещений особого значения не имеет,
  если мы сумеем избежать проблемы насыщения.
  Некоторые даже пытаются инициализировать все смещения нулём,
  и полагаются на то, что градиентный спуск сможет выучить подходящие смещения.
  Но поскольку вероятность того, что это на что-то повлияет, мала,
  мы продолжим использовать ту же процедуру инициализации, что и ранее.
  
  Давайте сравним результаты старого и нового подходов инициализации весов
  с использованием задачи по классификации цифр из MNIST.
  Как и ранее, мы будем использовать 30 скрытых нейронов,
  мини-пакет размером в 10, параметр регуляризации &lambda=5,0,
  и функцию стоимости с перекрёстной энтропией.
  Мы будем постепенно уменьшать скорость обучения с ?=0,5 до 0,1,
  поскольку так результаты будут немного лучше видны на графиках.
  Обучаться можно с использованием старого метода инициализации весов:
  
  ......
  
  Также можно обучаться при помощи нового подхода к инициализации весов.
  Это даже проще, ведь по умолчанию network2 инициализирует веса
  при помощи нового подхода.
  Это значит, что мы можем опустить вызов net.large_weight_initializer() ранее:
  
  .......
  
  Строим график (при помощи программы weight_initialization.py):
  
  ......
  
  В обоих случаях получается точность классификации в районе 96%.
  Итоговая точность почти совпадает в обоих случаях.
  Но новая техника инициализации доходит до этой точки гораздо, гораздо быстрее.
  В конце последней эпохи обучения старый подход к инициализации весов
  достигает точности в 87%,
  а новый подход уже подходит к 93%.
  //// Судя по графику в тексте должно стоять "в конце первой эпохи обучения"
  //// а не "последней эпохи". Но что есть, то есть.
  Судя по всему, новый подход к инициализации весов
  начинает с гораздо лучшей позиции,
  благодаря чему мы получаем хорошие результаты гораздо быстрее.
  //// Очень полезная информация.
  //// Получается, что "одну девятку" можно получить уже
  //// в рамках одной эпохи обучения.
  //// И в этом вся сила "обратного распространения ошибки" с регуляризацией,
  //// и более разумной инициализацией.
  //// А вот то, что происходит в последующих эпохах "упрямо твердит",
  //// что что-то нужно "менять в консерватории".
  То же явление наблюдается,
  если построить результаты для сети с 100 нейронами:
  //// Т.е. это мало зависит и от масштаба сети и, возможно, размера датасета,
  //// а отражает во "всем великолепии" "блеск и нищету"
  //// существующих методов машинного обучения.
  //// "В грамм добыча - в годы труды".
  //// Но ведь в начале сеть обучалась достаточно быстро,
  //// так что же происходит за рамками первых двух-трех эпох обучения?
  //// Опять же тема, которую как-то избегают обсуждать.
  
  ......
  
  В данном случае две кривые не встречаются.
  Однако мои эксперименты говорят, что если добавить ещё немножко эпох,
  то точности начинают почти совпадать.
  Поэтому на базе этих экспериментов можно сказать,
  что улучшение инициализации весов только ускоряет обучение,
  но не меняет итоговой эффективности сети.
  Однако в главе 4 мы увидим примеры НС,
  у которых долгосрочная эффективность серьёзно улучшается
  в результате инициализации весов через 1/√nin.
  Поэтому, улучшается не только скорость обучения,
  но иногда и итоговая эффективность.
  
  Подход к инициализации весов через 1/√nin
  помогает улучшать обучение нейросетей.
  Предлагались и другие техники инициализации весов,
  многие из которых основываются на этой базовой идее.
  Не буду рассматривать их здесь,
  поскольку для наших целей хорошо работает и 1/√nin.
  Если вам интересно, порекомендую почитать обсуждение
  на страницах 14 и 15 в работе от 2012 года за авторством Йошуа Бенджио.
  http://arxiv.org/pdf/1206.5533v2.pdf
  
  ......
  
  Возвращаемся к распознаванию рукописных цифр: код
  
  ......
  
  Среди более интересных изменений - включение L2 регуляризации.
  Хотя это большое концептуальное изменение,
  его настолько легко реализовать, что вы могли не заметить это в коде.
  По большей части, это просто передача параметра lmbda разным методам,
  особенно Network.SGD.
  Вся работа проводится в одной строчке программы,
  четвёртой с конца в методе Network.update_mini_batch.
  Там мы изменяем правило обновления градиентного спуска,
  чтобы оно включало ослабление веса.
  Изменение крохотное, но серьёзно влияющее на результаты!
  
  Это, кстати, часто бывает при реализации новых техник в нейросетях.
  Мы тысячи слов потратили на обсуждение регуляризации.
  Концептуально это довольно тонкая и сложная для понимания вещь.
  Однако её можно тривиальным образом добавить к программе!
  Неожиданно часто сложные техники получается реализовывать
  с небольшими изменениями кода.
  //// А с другой стороны, такое простое включение, может приводить
  //// и, скорее всего, приводит к тому, что понимание того
  //// какую собственно регуляризацию, когда именно и как именно
  //// применять в каждом конкретном случае,
  //// уже не так чтобы и требуется.
  //// Можно просто попытаться "поиграться" с подключением разных функций
  //// и посмотреть на конечные результаты.
  //// Вот так и воспитывается школа "истинных шаманов датасатанизма".
  
  ......
  
  //// Из комментариев.
  
  masai
  15 июл 2019 в 15:24
  
  Со стороны канонической математики вся эта эвристика выглядит как
  - <подогнать теорию под результат>
  
  Ну так это так и работает, это именно математика.
  У нас есть генеральная совокупность
  и какое-то априорное предположение о его распределении.
  Это может быть как вера в бритву Оккама,
  так и какое-то следствие из теории, описывающей предметную область.
  
  Если мы подбираем параметры модели так,
  чтобы результаты наблюдений имели наибольшую вероятность,
  то это метод максимального правдоподобия.
  Вот он как раз и переобучается.
  И это не очень логично, использовать такой метод.
  У нас же есть какие-то априорные предположения,
  а мы их не используем.
  
  А если мы применим теорему Байеса и учтём наши априорные знания,
  то получится оценка апостериорного максимума.
  //// К сожалению, автор этого комментария не дал более точных указаний
  //// как получить эту "оценку апостериорного максимума"
  //// для задач регуляризации нейросетей,
  //// а моих знаний не хватает, чтобы хотя бы представить ее общий вид.
  Если расписать функцию потерь в этом случае,
  то как раз и появится регуляризационное слагаемое.
  //// Вера в "единственно верную функцию" детектед.
  //// Дело не "максимальном правдоподобии" или "Байесе",
  //// вопрос в том почему "регуляризация" имеет положительный эффект,
  //// а не, наоборот, отрицательный -
  //// ведь мы, по факту, принудительно "смещаем прицел"
  //// "главного калибра" машинного обучения
  //// - метода обратного распространения ошибки.
  
  Рассуждения про бритву Оккама - это здорово,
  но они не объясняют с математической точки зрения,
  почему нужна регуляризация и почему она именно такая.
  А с помощью теоремы Байеса это всё отлично выводится.
  
  Скажем, L2 - это не просто из головы взяли, что надо в квадрат возводить.
  Этот регуляризационный член появляется
  из априорного предположения о нормальности распределения шума.
  Так что это очень даже каноничная математика.
  //// А откуда известно о "нормальном распределении шума",
  //// допустим, в идеально чистых данных?
  //// А регуляризация и в этом случае дает какой-то прирост качества обучения.
  
  С другой стороны, если модель очень сложная,
  то мало кто заморачивается с выводами
  (так как иногда исследование просто не провести из-за запредельной сложности),
  а просто экспериментируют. :)
  Но понимать, откуда у регуляризации ноги растут, я думаю, полезно.
  //// А это понимание действительно есть?
  //// Или все объясняется "гауссовским шумом"?
  
  phenik
  17 июл 2019 в 17:58
  
  Рассуждения автора во вводной части представляются спорными.
  Он сравнивает два метода описания экспериментальных данных
  - аппроксимацию с помощью многочлена и линейную регрессию.
  И задается вопросом, какой метод даст лучшие предсказания
  за пределами области определения данных.
  Но это разные задачи,
  и кажется странным сравнивать результаты предсказания по ним.
  Первая, как можно точнее описать поведение данных,
  вторая - выявить тренд.
  Соответственно, если обучать нейросеть,
  то и критерии должны быть разными.
  К этому никакого отношения не имеет бритва Оккама.
  Если задаться вопросом - какая зависимость имеет место в действительности,
  если это результаты измерений,
  то для этого нужно понять происхождение отклонений.
  Связано это с ошибками измерений,
  или присуще поведению самого явления?
  На практике это бывает иногда сложно понять.
  Но в этом и состоит работа экспериментатора.
  
  Дальнейшие рассуждения с бритвой Оккама кажутся вовсе ошибочными.
  Пример с частицей Шейна, в полном соответствии с принципом Оккама,
  не сложнее описывается, как утверждает автор, а именно проще.
  Для каждой из пластин Бете находит объяснение не привлекающее излишнюю сущность
  - новую частицу,
  т.е. его описание проще, а не сложнее.
  Второй пример, с гравитацией, вообще не подпадает
  под действие этого методологического принципа,
  т.к. закон тяготения Ньютона и ОТО находятся
  во взаимоотношении регулируемом принципом соответствия,
  и первый является частным случаем второй,
  в предельном случае слабого гр. потенциала.
  
  ......
  
  никто пока ещё не разработал полностью убедительное
  теоретическое объяснение тому,
  почему регуляризация помогает сетям проводить обобщение.
  
  Если исходить из обучения человека,
  имея ввиду, в общем случае и процесс познания, как самообучения,
  то обобщению предшествует процедура абстрагирования,
  связанная с удалением несущественных признаков.
  В этом смысле регуляризацию в НС можно рассматривать,
  как аналог абстрагирования у человека.
  //// Еще одна версия объяснения "регуляризации"
  //// на основе аналогии с обучением человека.
  Если посмотреть на определение абстрагирования,
  то можно увидеть многообразие ее разновидностей.
  То есть и здесь нет единого механизма,
  к которому сводилась бы эта процедура.
  Возможно поэтому нет и единого математического описания регуляризации.
  Но это предположение,
  математики не раз находили решение таких сложных задач)
  
  На абстрагирование, и регуляризацию в НС,
  можно посмотреть с другой точки зрения,
  как на сжатие информации с потерями.
  В какой-то момент эволюция установила,
  что условные рефлексы полезно вырабатывать
  не только для целостных сенсорных образов,
  но и для примитивов сжатия.
  Это подстегнуло процесс дальнейшего сжатия информации,
  вплоть до появления абстрактных понятий у человека,
  и их использования в процессе мышления.
  Таким образом, эволюция решила сразу две задачи,
  произвела сжатие информации, уменьшив требования к вычислительным ресурсам мозга,
  и нашла результатам сжатия применение во все более усложняющемся мышлении,
  как полезного приспособления для выживания видов.
  
  А это особенно досадно,
  поскольку в обычной жизни люди феноменально хорошо умеют обобщать данные:
  Думаю, что в будущем мы выработаем боле мощные технологии регуляризации
  в искусственных нейросетях,
  техники, которые в итоге позволят НС обобщать данные,
  исходя из ещё менее крупных наборов данных.
  
  Да, а для начала хотя бы реализовать в НС эту способность,
  которая имеется уже у детей)
  
  ..........
  
  //// Конец цитирования.
  
  В общем, как-то не убедил меня ни варианты объяснения "регуляризации"
  ни на основе "обобщающей способности", ни на основе "гауссовского шума",
  если так можно классифицировать приведенные выше объяснения.
  А вот возможность попытаться объяснить "эффект регуляризации"
  на руководствуясь концепцией представления нейросети
  как "ансамбля ансамблей", т.е. комбинации/наложения множества подсетей
  всерьез заинтересовала и увлекла.
  
  Но, наверно, внятно изложить ее в паре абзацев не получится,
  а этот фрагмент и так уж получился очень объемным,
  так что я попытаюсь сформулировать и обосновать эту концепцию
  в отдельном фрагменте.
  Вот только надо постараться "собрать в кучку" достаточно разрозненные мысли,
  и как-то это еще и более-менее последовательно изложить.
  
  
  =========
  
  13.04.2024 11:56
  
  Попытка неканонической трактовки регуляризации. Часть 1.
  В оглавление.
  
  Давйте попробуем взглянуть на возможность объяснения "эффекта регуляризации"
  с точки зрения представления обучения и работы нейросети
  не как единого целого, а как суммы работы "ансамбля ансамблей",
  т.е. "сети подсетей".
  И посмотрим может ли такой подход что-то не только объяснить,
  но и подсказать что-то более-менее интересное/практичное/полезное.
  
  Итак, "начнем от печки".
  Имеется некая система, фактически "черный ящик", называемый нейросетью,
  и набор обучающих данных, возможно, не очень чистых - точно мы об этом не знаем.
  И нам требуется, чтобы "черный ящик" не только запомнил именно эти данные,
  но и был способен их "обобщить",
  т.е. давать верные результаты не только на этих данных,
  но и на других данных из этой же области.
  Причем, априорно неизвестно ни о каких-то закономерностях в этих данных,
  ни степень их "чистоты", т.е. наличия в них какого-то "шума".
  Вся надежда на "магию обобщения" этого "черного ящика".
  И все оправдания, когда это получается как-то "не очень",
  сводятся то ли к "переобучению", то ли к "недообучению",
  а в итоге недостаточном размере то ли "обучающих данных",
  то ли "черного ящика", то ли времени "обучения,
  то ли, вообще, "благосклонности небес".
  
  Ну, насчет "благосклонности небес" спорить глупо
  - это, наверно, всегда очень существенный фактор и от нас не зависит.
  А вот все остальное уже область нашей ответственности,
  помноженной на степень нашего понимания происходящего.
  Иначе не остается ничего другого как следовать рекомендациям
  из известного анекдота про "английские газоны":
  "поливать" - добавлять все больше и больше обучающих данных -
  и "стричь" - регуляризировать и варьировать гиперпараметры -
  "и так триста лет".
  
  А можно ли как-то по-другому?
  Давайте разбираться.
  Обычно описание обучение/работу нейросетей пытаются делать
  на примере линейной регрессии, так как это приведено в предыдущем фрагменте.
  Вот только проблема в том, что это описание как правило сводится
  к рекомендациям желательности получить более простую систему с "шумом",
  а вот относительно получения более сложной системы отношение неоднозначное.
  Ну, к вопросу "шума" еще вернемся.
  А пока отметим, что чисто исторически такая трактовка объяснения работы нейросетей
  сложилась еще "во времена Очакова и покоренья Крыма",
  то бишь, исходного представления нейросети классическим перцептроном
  с одним скрытым слоем и ЛИНЕЙНЫМ выходным слоем.
  Причем количество нейронов/связей в таких сетях не сильно превышало
  размерность входных данных,
  и по этому сопоставление обучения/работы такой нейросети
  достаточно близко соответствовало алгоритмам многомерной линейной регрессии.
  Интересно, что и качество работы таких нейросетей
  достаточно близко соответствовало многомерной линейной модели с "шумом".
  С тех пор "много воды утекло в Стиксе",
  архитектуры нейросетей и методы их обучения существенно изменились,
  а такая трактовка осталась практически неизменной.
  Хотя к существующим архитектурам, на мой взгляд,
  это уже мало применимо.
  И вот почему.
  
  Не будем нарушать "сложившуюся традицию" и попытаемся рассмотреть,
  что же собственно стоит изменить при рассмотрении нейросетей,
  с точки "линейной регрессии", но уже не "классической".
  Во-первых, это то, что масштаб нейросетей даже самых простых стал таким,
  что количество "свободных параметров", т.е. количества нейронов и связей,
  существенно превосходят размерность входных и выходных данных,
  а иногда и количества обучающих данных.
  И уже только поэтому обычное представление решение
  как суммы ограниченного количества слагаемых:
  y = a0 + a1*x**1 + a2*x**2+...+an*x**n
  можно поставить под сомнение.
  
  Тут можно было бы указать еще и на то,
  что если бы такое решение и соответствовало бы реальным сложным зависимостям
  обучающих данных - с "шумом" или без "шума" не так важно -
  то "феномена нейросетей" никто бы даже не заметил
  Проще было бы вычислить такой многочлен
  и не мучиться ни с непредсказуемым обучением нейросетей,
  ни с весьма затратным их инференсом.
  Однако же нейросети такое "простое решение" переиграли по всем параметрам,
  хотя в начале своей истории ничего подобного никак не демонстрировали.
  И это связано с началом использованием в архитектуре нейросетей
  НЕЛИНЕЙНЫХ элементов, хотя бы в форме "сигмоидных нейронов", слоев "softmax" и др.
  
  Вот держа эту "нелинейность" в уме, попробуем снова вернуть
  к "традиционной трактовке" нейросетей как "линейной регрессии".
  В прошлом фрагменте я пытался отметить, то место в "каноничесокй трактовке"
  на котором рассмотрение вариантов возможностей представления решений
  обычно заканчивалось. А заканчивалось оно на варианте,
  когда составляется многочлен с количеством свободных параметров соответствующих
  количеству входных данных.
  Мол дальше рассматривать бессмысленно с математической точки зрения.
  Но если все-таки переступить через "каноны"
  и попробовать представить решение полиномом "стопятьсотого порядка"?
  
  Что запрещает такое сделать?
  То что нет однозначного решения?
  Но ведь это как раз соответствует реалиям нейросетей,
  в которой различная инициализация параметров,
  которые можно считать неким аналогом исходных данных,
  а еще и различная последовательность обучающих примеров
  приводят к различным конечным состояниям и разной результативности.
  То есть, принципиальных запретов на такое представление нет.
  
  Теперь делаем следующий шаг.
  Представим этот полином "стопятьсотого порядка"
  как сумму "триллиарда" полиномов различных степеней.
  Что такое представление не имеет смысла, так как все равно суммируются?
  Правильно, действительно не имеет,
  НО только при использовании в архитектуре нейросети
  ТОЛЬКО линейных элементов.
  А вот теперь вспоминаем про НЕЛИНЕЙНЫЕ элементы нейросети,
  и тогда представление результата в виде суммы отдельных полиномов
  "начинает играть новыми красками".
  Фактически каждый такой отдельный "полином" можно рассматривать
  как представление поведение какой-то внутренней "подсети" или "ансамбля параметров"
  целой нейросети.
  
  Т.е. нейросеть это как "муравейник", внутри которого живут "отдельные муравьи",
  причем многие "муравьи" "на поверхность могут никогда в жизни не вылезать",
  т.е. "подсети" совершенно необязательно представлять
  как какой-то "срез" всей сети от входного до выходного слоя.
  Вполне возможны чисто внутренние "подсети"/"ансамбли",
  имеющие связи только с глубокими слоями всей нейросети.
  
  Собственно, вся эта отсылка к "линейной регрессии" была только и не столько
  как попытка хоть как-то даже не обосновать такое представление нейросетей
  - понятно, что это достаточно "неформальное обоснование -
  а иллюстрацией того, что это никак не противоречит "математическим основам".
  Но зато такое представление нейросети, как "ансамбля ансамблей"
  позволяет хоть как-то подступиться к вопросам "эффектов регуляризации"
  чуть-чуть более конструктивно, чем в "канонической интерпретации".
  
  На этом будем считать "вводную часть" законченной,
  чтобы можно было идти дальше, не шарахаясь от термина "подсеть"/"ансамбль".
  
  Давайте попробуем представить как такие подсети формируются.
  Причем тут интересно самое начало обучения.
  Итак имеем случайным образом инициализированную нейросеть,
  которой предъявляется самый первый пример
  - для упрощения считаем, что размер мини-батча равен единице.
  Методом обратного распространения вносятся согласованные коррективы
  в большое количество связей всей нейросети,
  так чтобы выход этой сети при предъявлении этого же примера был бы
  в соответствии с функцией стоимости/ошибки лучше, чем предыдущий.
  Соответственно, следующий обучающий пример также можно рассматривать
  как приводящий из предыдущего состояния в обновленное.
  
  И теперь есть интересная возможность ПРЕДСТАВИТЬ
  это обновленное состояние нейросети либо как просто промежуточное состояние
  - как это обычно считается -
  а можно как некую виртуальную "подсеть" преобразующую предыдущее состояние
  - в данном случае первоначальную инициализацию -
  в какое-то более приемлемое с точки зрения процесса обучения реакцию
  для данных входных данных.
  
  Тут нужно вспомнить, что в отношении нейросети есть парочка "проклятых вопросов",
  не имеющим в настоящее время приемлемого решения.
  Это где и как именно нейросеть "запоминает" какие-то обучающие примеры,
  на которые обычно имеют ввиду при ссылках на пресловутое "переобучение".
  И, одновременно, "неясность" как именно реализуется не менее пресловутая
  "обобщающая способность" нейросети.
  
  Так вот, если рассматривать КАЖДОЕ преобразование параметров нейросети
  при обучении как отдельную "подсеть"/"ансамбль" ЗАПОМИНАЮЩИХ
  не сам обучающий пример,
  а именно преобразование предыдущего состояния в обновленное,
  то представляется, что можно через такое "несколько усложненное" представление
  подойти к "проклятым вопросам" чуть-чуть с другой стороны.
  
  Еще раз, допустив возможность представления обучения/реакции целой нейросети,
  как одновременною обучение/работу ОЧЕНЬ большого количества "подсетей"/"ансамблей",
  появляется возможность рассматривать эти "подсети"
  не только в пространственной "проекции", но и во ВРЕМЕННОЙ "проекции".
  Т.е. представлять работу нейросети как "овеществленную историю обучения",
  а не как обычное "мгновенное состояние".
  
  Зачем это надо?
  Пока точно сказать не могу - может ничего и не получится,
  этой идее/гипотезе еще и дня "от роду" нет.
  Но если вспомнить из истории науки какие последствия оказала
  казалось бы абстрактная идея "флюксий" - "исчисления бесконечно малых" -
  то можно предполагать, что попытка представить текущего поведения нейросетей
  как "КОНЕЧНОЙ суммы ИСТОРИИ обучения"
  тоже может оказать не совсем уж бесплодной.
  //// Еще одна уже техническая аналогия такого представления - БИХ-фильтры,
  //// фильтры с бесконечной импульсной характеристикой,
  //// только здесь в качестве элементов "памяти"/"обратной связи"
  //// используются/подразумеваются сами текущие значения параметров нейросети.
  Во всяком случае, хоть какое-то представление о том,
  как реализуется "память нейросети", такой подход обеспечивает/позволяет.
  И теперь интересно как это можно использовать.
  
  Первое, что приходит в голову это то,
  что рассматривая такую "временную последовательность подсетей",
  представляется, что влияние самых первых обучающих примеров
  имеет существенно большее значение чем последующих.
  Так как их "влияние" через эти "первые подсети" проявляется во всех последующих
  не только из-за их очередности,
  но, скорее всего, еще из-за того, что первоначальные изменения в "подсетях"
  из-за их первоначальной случайной инициализации еще и максимальна.
  
  А отсюда можно предположить,
  что последовательность первых обучающих примеров вовсе не так уж "безобидна".
  Представляется, что, наверно, первые обучающие примеры должны быть
  максимально разнообразны,
  или, наоборот, максимально близкими друг другу и к "среднему".
  Во всяком случае, интересно было бы почитать про такого рода эксперименты.
  Но обычно самое начало процесса обучения остается вне "фокуса внимания" исследователей,
  так как считается, что последующее обучение все равно все "выравнивает".
  и интересно только конечное качество.
  Но ведь с чего-то "переобучение" все-таки начинается,
  и может быть это закладывается на самых первых шагах обучения.
  А то что привязать это "переобучение" к конкретному обучающему примеру/примерам
  просто так никому не удается, так ведь большинство моделей обучается
  по случайной последовательности примеров,
  и никто не пытается как-то эту последовательность отслеживать.
  Наоборот, считается, что "Великий Рэндом" как раз и "дарует счастье".
  //// Честно говоря, я и сам так считал
  //// в начале всей этой эпопеи изучения нейросетей,
  //// но это было так давно - уже целый год назад,
  //// и с тех пор пришло понимание, что все намного-намного сложнее
  //// и, одновременно, проще, если пытаться использовать другие подходы.
  
  Вообще-то, мысль о том, что последовательность обучающих примеров
  имеет достаточно важное значение в конечном результате обучения,
  уже достаточно крутится в голове, и уже не раз озвучивалась.
  Так что, возможно, предыдущий абзац это попытка "подогнать"
  одну гипотезу под другую, без всякого объективного основания.
  Но можно взглянуть на это и по другому
  - как попытку выстроить систему взаимосвязанных гипотез,
  обеспечивающих преодоление какого барьера инерционного/информационного
  или просто догматического.
  
  Можно даже обосновать попытку выстроить такой "круг гипотез",
  сославшись на метод "кругового доказательства" Платона.
  Но при этом напомнить, что этот метод "настоящая наука" отвергает,
  следуя заветам/силлогизмам ученика Платона - Аристотеля.
  Хотя "метод Аристотеля" предполагает в качестве исходной точки рассуждении
  использовать некие "аксиомы" - "безусловные и непререкаемые истины".
  Но в том то и дело что в области машинного обучения нейросетей
  таких "аксиом" как-то не наблюдается.
  Практически под любой факт из этой области находится контраргумент.
  
  Поэтому "круг гипотез" отнюдь не самый плохой метод в такой ситуации.
  Очень рекомендую тем, кого заинтересовал такой подход к поиску решений
  в области, где отсутствуют нужная информация или новые идеи,
  наряду с очень высокими барьерами психологической инерции,
  для "домашнего чтения" короткий и хороший рассказ Валентины Журавлевой:
  "Приключение"
  https://libking.ru/books/sf-/sf/61185-valentina-zhuravleva-priklyuchenie.html,
  
  Так что я продолжу попытку выстраивать "круг гипотез".
  
  Рассмотрим первый, самый начальный этап обучения нейросети,
  так называемый инфляционный период" обучения,
  скажем, в рамках первой эпохи обучения,
  во время которого нейросети предъявляются все примеры из обучающего набора.
  В рамках канонической трактовки на этом этапе происходит
  начальное обучение нейросети путем последовательной настройки параметров.
  Причем улучшение качества сети практически линейно возрастает
  по мере предъявления новых примеров,
  и может достигать в конце первой эпохи точности в "одну девятку", то бишь, 90%.
  
  Как правило, этот этап обучения практически не исследуется,
  так как, вроде, и так все ясно - "сеть обобщает".
  Вопрос в том, как это происходит?
  Вообще-то существует, как минимум, два способа "обобщения".
  Первый, который наиболее желанный/приветствуемый и, одновременно, самый сложный,
  через выявление внутренних закономерностей, построения модели,
  и выдвижения гипотез на основе этой модели,
  с последующей ее корректировкой на основе сопоставления прогноза с реальностью.
  Второй, много проще, через простейшее "осреднение" неточных предсказаний.
  Та самая "мудрость толпы", тоже известная со времен Аристотеля.
  
  Так вот, рассматривая реакцию нейросети как сумму реакций
  "виртуально-исторических подсетей" становится совершенно очевидно,
  что этап "инфляции" при обучении нейросетей
  это явно проявление "мудрости толпы", то бишь,
  банального "усреднения" запомненных примеров.
  И тут в явном виде проявляется и закон "больших чисел"
  и "гауссовское распределение" ошибок,
  и применимость "модели с шумом".
  И причина здесь, в первую очередь в том,
  что несмотря на достаточно сильную связь между последовательными
  "виртуальными подсетями" они все-таки достаточно "разношерстны"
  из-за случайной начальной инициализации,
  и как следствие достаточно значительных отличий
  между начальным и конечным состоянием этих "виртуальных подсетей".
  И это соответствует большей "внутренней размерностью",
  обнаруживаемой/замеренной на начальном этапе обучения.
  
  Соответственно, представляется разумным на этом этапе
  обеспечивать максимально возможную "разношерстность", "внутреннюю размерность"
  самых первых примеров обучения, или даже всей первой эпохи обучения.
  и это вполне соответствует "каноническим" рекомендациям из предыдущего фрагмента
  о целесообразности сильного уменьшения инициализационных значений параметров,
  чтобы не ограничивать их вариабельность при обучении
  за счет попадания в "зону насыщения" нелинейных элементов.
  
  Т.е. в начале обучения предпочтительнее максимально линейная модель,
  обеспечивающая более быстрое и, наверно, более "гармоничное"/"многстороннее"
  достижение некоторого максимально возможного уровня качества для этапа "инфляции".
  Тут наверно, даже важнее "гармоничность"/"разнообразие" в ущерб "скорости"
  - все равно этот этам обучения нейросети самый непродолжительный -
  а вот какой при этом закладывается потенциал для успешности этапа "компрессии",
  толком похоже никто не исследовал.
  Но есть такое народные "наблюдения": "горбатого могила исправит"
  или "рожденный ползать - летать не может",
  и, наверно, стоит к ним прислушаться и при обучении нейросетей.
  
  Так что, может быть,
  на этом этапе - начального обучения - вклад регуляризации и дропаута
  стоит даже существенно увеличить.
  Но это только "теорехтическое" предположение и, возможно, неверное,
  так как пока никаких экспериментальных/подтверждающих данных по этому вопросу,
  мне лично, пока не встречалось,
  Но это предположение основывается на том, как представляется работа
  механизмов регуляризации и дропаута в рамках концепции "ансамбля ансамлей".
  
  Проще всего описать эти представления по отношению к методам "дропаута".
  Исключение каких-то связей при обновлении весов по факту создает
  новую "ветку" дальнейшего развития "подсетей", явно повышая их "разнообразие",
  "разношерстность" и в итоге внутреннюю размерность нейросети.
  Соответственно, это больше приближает эти "подсети" к интерпретации "независимые",
  и, следовательно, больше соответствуя концепции "больших чисел",
  то бишь, какому-то стандартному вероятностному распределению.
  Вопрос: "какому именно" - "гауссовскому", "равномерному", "биномиальному" и т.п., -
  явно не по моим математическим способностям.
  Но, в любом случае, это должно/может способствовать большему внутреннему
  "разнообразию" "подсетей"/"ансамблей",
  которое, наверняка, как-то пригодится на этапе "компрессии",
  просто за счет большего количества "степеней свободы".
  Но опять же, это больше "теорехтическое" предположение.
  
  Для регуляризаций типа L1 или L2 представление немного посложнее.
  С одной стороны, их можно рассматривать и в русле "канонической трактовки",
  как смещение и параметров/весов в каком-то предположительно желаемом направлении,
  а с другой стороны, и как своеобразный способ "аугментации" данных,
  с целью их большего "разнообразия",
  а с третьей стороны, как достаточно "прямолинейную" попытку,
  добиться от нейросети "обобщающих свойств", на основе предположения о том,
  как это "обобщение" реализуется.
  И так как эти методы как-то практически работают,
  то любое из этих представлений имеет под собой основание.
  
  Но какое-то интуитивное чувство, что регуляризации типа L1 или L2
  на этапе "инфляции" следует применять значительно осторожнее, чем дропаут.
  Это скорее инструмент этапа "компрессии",
  когда требуется довести точность нейросети до уровня "две девятки",
  за счет внутреннего согласования "подсетей".
  "Но это не точно".
  
  Вообще-то, и время на сегодня кончается, и "кувшин моих мыслей показывает дно",
  так что запланированное рассмотрение этапа "компрессии" нейросети
  сегодня никак уже не получается.
  Да и следующий фрагмент учебника по глубокому обучению тоже стоило бы
  предварительно проработать, чтобы подойти к самой "мякотке"
  - регуляризации на этапе "компрессии" -
  собственно к тому, что больше всего интересует "адептов" машинного обучения.
  
  Так что на этом я и закончу эту часть описания концепции "ансамбля ансамблей"
  в приложении к механизмам работы "эффектов регуляризации".
  Продолжение следует.
  
  
  =========
  
  14.04.2024 10:42
  
  Таинства подбора гиперпараметров обучения нейросетей.
  В оглавление.
  
  Все теоретические знания, требуют проверки практикой,
  и то насколько "теория нейронауки" далека от практики,
  по-настоящему можно ощутить только при реальном обучении нейросети,
  когда нужно из каких-то соображений выбрать какое-то конкретное значение:
  
  "Нейросети и глубокое обучение, глава 3, ч.3:
  как выбрать гиперпараметры нейросети?"
  Автор: SLY_G (Вячеслав Голованов)
  https://habr.com/ru/articles/460711/.
  22 июл 2019 в 07:00
  
  Автор оригинала: Michael Nielsen
  http://neuralnetworksanddeeplearning.com/chap3.html#why_does_regularization_help_reduce_overfitting
  
  //// Начало цитирования.
  
  До сих пор я не объяснял, как я выбираю значения гиперпараметров
  - скорость обучения ?, параметр регуляризации ?, и так далее.
  Я просто выдавал неплохо работающие значения.
  На практике же, когда вы используете нейросеть для атаки на проблему,
  может быть сложно найти хорошие гиперпараметры.
  Представьте, к примеру, что нам только что рассказали о задаче MNIST,
  и мы начали работать над ней, ничего не зная
  по поводу величин подходящих гиперпараметров.
  Допустим, что нам случайно повезло,
  и в первых экспериментах мы выбрали многие гиперпараметры так,
  как уже делали в этой главе:
  30 скрытых нейронов, размер мини-пакета 10, обучение за 30 эпох
  и использование перекрёстной энтропии.
  Однако мы выбрали скорость обучения ?=10,0,
  и параметр регуляризации ?=1000,0.
  И вот, что я увидел при таком прогоне:
  
  .......
  
  Наша классификация работает не лучше случайной выборки!
  Наша сеть работает как генератор случайного шума!
  
  <Ну, это легко поправить, - могли бы сказать вы,
  - просто уменьшите такие гиперпараметры,
  как скорость обучения и регуляризацию>.
  К сожалению, априори у вас нет информации по поводу того,
  что именно эти гиперпараметры вам нужно подстроить.
  Может быть, главная проблема в том,
  что наши 30 скрытых нейронов никогда не будут работать,
  вне зависимости от того, как выбираются остальные гиперпараметры?
  Может, нам надо не меньше 100 скрытых нейронов? Или 300?
  Или множество скрытых слоёв?
  Или другой подход к кодированию выхода?
  Может, наша сеть учится, но надо обучать её больше эпох?
  Может, размер мини-пакетов слишком мал?
  Может, у нас получилось бы лучше,
  вернись мы к квадратичной функции стоимости?
  Может, нам надо попробовать другой подход к инициализации весов?
  И так далее, и тому подобное.
  В пространстве гиперпараметров легко потеряться.
  И это может доставить реально много неудобств,
  если ваша сеть очень большая,
  или использует огромные объёмы обучающих данных,
  и вы можете обучать её часы, дни или недели,
  не получая результатов.
  В такой ситуации ваша уверенность начинает сдавать.
  Может, нейросети были неправильным подходом для решения вашей задачи?
  Может, вам уволиться и заняться пчеловодством?
  
  В данном разделе я объясню некоторые эвристические подходы,
  которые можно использовать для настройки гиперпараметров в нейросети.
  Цель - помочь вам выработать рабочий процесс,
  позволяющий вам достаточно хорошо настраивать гиперпараметры.
  Конечно, я не смогу покрыть всю тему оптимизации гиперпараметров.
  Это огромная область, и эта не та задача,
  которую можно решить полностью,
  или по правильным стратегиям решения которой имеется всеобщее согласие.
  Всегда есть возможность попробовать ещё какой-нибудь трюк,
  чтобы выжать дополнительные результаты из вашей нейросети.
  Но эвристика в данном разделе должна дать вам отправную точку.
  
  Общая стратегия
  
  При использовании нейросети для атаки новой проблемы,
  первая сложность - получить от сети нетривиальные результаты,
  то есть, превышающие случайную вероятность.
  Это может оказаться удивительно сложным делом,
  особенно когда вы сталкиваетесь с новым классом задач.
  Давайте посмотрим на некоторые стратегии,
  которые можно использовать при подобном затруднении.
  
  Допустим, к примеру, что вы впервые атакуете задачу MNIST.
  Вы начинаете с большим энтузиазмом,
  но полная неудача вашей первой сети вас немного обескураживает,
  как это описано в примере выше.
  Тогда нужно разобрать проблему по частям.
  Нужно избавиться от всех обучающих и подтверждающих изображений,
  кроме изображений нулей и единиц.
  Потом попытаться обучить сеть отличать 0 от 1.
  Эта задача не только по существу проще, чем различение всех десяти цифр,
  она также уменьшает количество обучающих данных на 80%,
  ускоряя обучение в 5 раз.
  Это позволяет гораздо быстрее проводить эксперименты,
  и даёт вам возможность быстрее понять, как создать хорошую сеть.
  
  Можно ещё сильнее ускорить эксперименты,
  низведя сеть до минимального размера,
  который с большой вероятностью сможет осмысленно обучаться.
  Если вы считаете, что сеть [784, 10] с большой вероятностью
  сможет классифицировать цифры MNIST лучше, чем случайная выборка,
  то начинайте эксперименты с неё.
  Это будет гораздо быстрее, чем обучать [784, 30, 10],
  а до неё уже можно дорасти потом.
  
  Ещё одно ускорение экспериментов можно получить,
  увеличив частоту отслеживания.
  В программе network2.py мы отслеживаем качество работы в конце каждой эпохи.
  Обрабатывая по 50 000 изображений за эпоху,
  нам приходится ждать довольно долго
  - примерно по 10 секунд на эпоху на моём ноутбуке при обучении сети [784, 30, 10]
  - перед тем, как получить обратную связь о качестве обучения сети.
  Конечно, десять секунд - это не так уж и долго,
  но если вы хотите попробовать несколько десятков разных гиперпараметров,
  это начинает раздражать,
  а если вы хотите попробовать сотни или тысячи вариантов,
  это уже просто опустошает.
  Обратную связь можно получать гораздо быстрее,
  отслеживая точность подтверждения чаще,
  например, каждые 1000 обучающих изображений.
  Кроме того, вместо использования полного набора
  из 10 000 подтверждающих изображений,
  мы можем получить оценку гораздо быстрее,
  используя всего 100 подтверждающих изображений.
  Главное, чтобы сеть увидела достаточно изображений,
  чтобы реально обучаться,
  и чтобы получить достаточно хорошую оценку эффективности.
  Конечно, наша network2.py пока не обеспечивает такого отслеживания.
  Но в качестве костылей для достижения такого эффекта
  в целях иллюстрации, мы обрежем наши обучающие данные
  до первых 1000 изображений MNIST.
  Попробуем посмотреть, что происходит
  (для простоты кода я не использовал идею того,
  чтобы оставить только изображения 0 и 1
  - это тоже можно реализовать, приложив чуть больше усилий).
  
  ........
  
  Всё ещё получаем чистый шум, но у нас есть большое преимущество:
   обратная связь обновляется за доли секунды, а не каждые десять секунд.
  Это значит, вы можете гораздо быстрее экспериментировать
  с выбором гиперпараметров,
  или даже проводить эксперименты со множеством разных гиперпараметров
  почти одновременно.
  
  В примере выше я оставил значение ? равным 1000,0, как и раньше.
  Но поскольку мы изменили количество обучающих примеров,
  нам надо поменять и ?, чтобы ослабление весов было тем же.
  Это значит, что мы меняем ? на 20,0.
  В таком случае получится следующее:
  
  ........
  
  Ага! У нас есть сигнал.
  Не особенно хороший, но есть.
  Это уже можно взять за отправную точку, и изменять гиперпараметры,
  чтобы попробовать получить дальнейшие улучшения.
  Допустим, мы решили, что нам надо поднять скорость обучения
  (как вы, наверно, поняли, решили мы неправильно,
  по причине, которую мы обсудим позднее,
  но давайте пока попробуем так сделать).
  Для проверки нашей догадки мы выкручиваем ? до 100,0:
  
  ........
  
  Всё плохо! Судя по всему, наша догадка была неверна,
  и проблема была не в слишком низком значении скорости обучения.
  Пытаемся подкрутить ? до небольшого значения 1,0:
  
  .......
  
  //// Интересно, что в этом примере результаты первой эпохи лучше чем последующих.
  //// Случайность, скорее всего, но может быть в этом что-то есть.
  //// Во всяком случае, используемая регуляризация на первом этапе
  //// явно не должна быть слишком сильной.
  
  Вот так лучше! И так мы можем продолжать и далее,
  подкручивая каждый гиперпараметр,
  и постепенно улучшая эффективность.
  Изучив ситуацию и найдя улучшенное значение для ?,
  мы переходим к поиску хорошего значения для ?.
  Затем проведём эксперимент с более сложной архитектурой,
  допустим, с сетью из 10 скрытых нейронов.
  Затем вновь подстроим параметры для ? и ?.
  Затем увеличим сеть до 20 скрытых нейронов.
  Ещё немного подстроим гиперпараметры.
  И так далее, оценивая на каждом шаге эффективность
  при помощи части наших подтверждающих данных,
  и используя эти оценки для подбора всё лучших гиперпараметров.
  В процессе улучшений на то, чтобы увидеть влияние подстройки гиперпараметров,
  уходит всё больше времени,
  поэтому мы можем постепенно уменьшать частоту отслеживания.
  
  В качестве общей стратегии такой подход выглядит многообещающе.
  Однако я хочу вернуться к тому первому шагу поиска гиперпараметров,
  позволяющих сети вообще хоть как-то обучиться.
  На самом деле, даже в приведённом примере
  ситуация получилась слишком оптимистичной.
  Работа с сетью, которая ничему не обучается,
  может оказаться чрезвычайно раздражающей.
  Можно по нескольку дней подстраивать гиперпараметры,
  и не получать осмысленных ответов.
  Поэтому я хотел бы ещё раз подчеркнуть,
  что на ранних стадиях нужно убедиться,
  что вы можете получать быструю обратную связь от экспериментов.
  Интуитивно может казаться,
  что упрощение проблемы и архитектуры вас лишь замедлит.
  На самом деле, это ускоряет процесс,
  поскольку вы сможете гораздо быстрее найти сеть с осмысленным сигналом.
  Получив такой сигнал, вы часто сможете получать быстрые улучшения
  при подстройке гиперпараметров.
  Как и во многих жизненных ситуациях, самым сложным бывает начать процесс.
  //// "Дорога в десять тысяч лье, начинается с первого шага".
  //// И этот шаг, желательно, чтобы был хоть в чем-то результативным.
  //// Иначе это может быть "топтанием на месте", а не "началом Пути".
  
  Ладно, это общая стратегия.
  Теперь давайте взглянем на конкретные рекомендации
  по назначению гиперпараметров.
  Сконцентрируюсь на скорости обучения ?, параметре регуляризации L2 ?,
  и размере мини-пакета.
  Однако многие замечания будут применимы и к другим гиперпараметрам,
  включая связанные с архитектурой сети,
  другими формами регуляризации,
  и некоторые гиперпараметры, с которыми мы познакомимся в книге далее,
  например, коэффициент импульса.
  
  Скорость обучения
  
  Допустим, мы запустили три сети MNIST с тремя разными скоростями обучения,
  ?=0,025, ?=0,25 и ?=2,5, соответственно.
  Остальные гиперпараметры мы оставим такими,
  какие они были в прошлых разделах - 30 эпох,
  размер мини-пакета 10, ?=5,0.
  Мы также вернёмся к использованию всех 50 000 обучающих изображений.
  Вот график, показывающий поведение стоимости обучения
  (создан программой multiple_eta.py):
  
  ......
  
  При ?=0,025 стоимость уменьшается плавно до последней эпохи.
  С ?=0,25 стоимость сначала уменьшается,
  однако после 20 эпох она насыщается,
  поэтому большая часть изменений оказываются малыми
  и, очевидно, случайными колебаниями.
  С ?=2,5 стоимость сильно колеблется прямо с самого начала.
  Чтобы понять причину этих колебаний,
  вспомним, что стохастический градиентный спуск
  должен постепенно спускать нас в долину функции стоимости:
  
  .......
  
  Данная картинка помогает интуитивно представить происходящее,
  но не является полным и всеобъемлющим объяснением.
  Если говорить точнее, но кратко,
  то градиентный спуск использует аппроксимацию первого порядка
  для функции стоимости,
  чтобы понять, как уменьшать стоимость.
  Для более крупных ? становятся более важными
  члены функции стоимости более высокого порядка,
  и они могут доминировать в поведении, ломая градиентный спуск.
  Это особенно вероятно при приближении к минимумам
  и локальным минимумам функции стоимости,
  поскольку рядом с такими точками градиент становится маленьким,
  из-за чего членам более высокого порядка становится легче доминировать.
  
  Однако, если ? будет слишком крупной, то шаги будут такими большими,
  что могут перепрыгнуть минимум, из-за чего алгоритм
  будет карабкаться вверх из долины.
  Вероятно, именно это и заставляет стоимость осциллировать при ?=2,5.
  Выбор ?=0,25 приводит к тому,
  что начальные шаги действительно ведут нас по направлению
  к минимуму функции стоимости,
  и только когда мы подбираемся к нему,
  мы начинаем испытывать трудности с перепрыгиванием.
  А когда мы выбираем ?=0,025,
  у нас таких трудностей в течение первых 30 эпох нет.
  Конечно, выбор такого малого значения ? создаёт другую трудность
  - а именно, замедляет стохастический градиентный спуск.
  Лучшим подходом было бы начать с ?=0,25, обучиться 20 эпох,
  а потом перейти к ?=0,025.
  Позднее мы обсудим такую переменную скорость обучения.
  А пока остановимся на вопросе поиска
  одного подходящего значения для скорости обучения ?.
  
  Имея это в виду, мы можем выбрать ? следующим образом.
  Сначала мы оцениваем пороговое значение ?,
  при котором стоимость обучающих данных сразу начинает уменьшаться,
  а не колеблется и не увеличивается.
  Эта оценка не обязательно должна быть точной.
  Оценить порядок можно, начав с ?=0,01.
  Если стоимость уменьшается в первые несколько эпох,
  то стоит последовательно пробовать ?=0,1, затем 1,0,
  и так далее, пока не найдётся значение,
  при котором стоимость колеблется или увеличивается в первые эпохи.
  И наоборот, если стоимость колеблется
  или увеличивается в первые эпохи при ?=0,01,
  тогда попробуйте ?=0,001, ?=0,0001,
  пока не найдёте значение,
  при котором стоимость уменьшается в первые несколько эпох.
  Эта процедура даст вам порядок порогового значения ?.
  По желанию, вы можете уточнять вашу оценку,
  выбрав наибольшее значение для ?,
  при котором стоимость уменьшается в первые эпохи,
  например, ?=0,5 или ?=0,2
  (сверхточность тут не нужна).
  Это даёт нам оценку порогового значения ?.
  
  Реальное значение ?, очевидно, не должно превышать выбранное пороговое.
  На самом деле, чтобы значение ? оставалось полезным в течение многих эпох,
  вам лучше использовать значение раза в два меньше порогового.
  Такой выбор обычно позволит вам обучаться множество эпох
  без сильного замедления обучения.
  
  В случае данных MNIST следование этой стратегии приведёт
  к оценке порогового порядка значения ? в 0,1.
  После некоторых уточнений мы получим значение ?=0,5.
  Следуя приведённому выше рецепту,
  нам следует использовать значение ?=0,25 для нашей скорости обучения.
  Но на самом деле я обнаружил, что значение ?=0,5 хорошо работало
  в течение 30 эпох,
  поэтому я не беспокоился по поводу его уменьшения.
  
  Всё это выглядит довольно прямолинейно.
  Однако использование стоимости обучения для выбора ?,
  кажется, противоречит тому, что я говорил ранее
  - что мы выбираем гиперпараметры,
  оценивая эффективность сети при помощи избранных подтверждающих данных.
  На самом деле мы будем использовать точность подтверждения,
  чтобы подобрать гиперпараметры регуляризации, размер мини-пакета
  и такие параметры сети, как количество слоёв и скрытых нейронов,
  и так далее.
  Почему мы делаем всё по-другому в случае скорости обучения?
  Честно говоря, этот выбор обусловлен
  моими личными эстетическими предпочтениями,
  и, вероятно, предвзят.
  Аргументация такова,
  что другие гиперпараметры должны улучшать конечную точность классификации
  на проверочном наборе,
  поэтому имеет смысл выбирать их на основе точности подтверждения.
  Однако скорость обучения
  лишь косвенно влияет
  на конечную точность классификации.
  //// А вот тут, у меня есть сомнения. Но пока без особых оснований.
  //// Но "узелок на память" стоит завязать.
  Её основная цель - контролировать размер шага градиентного спуска,
  и отслеживать стоимость обучения наилучшим образом
  для того, чтобы распознавать слишком большой размер шага.
  Но всё же это личное эстетическое предпочтение.
  На ранних стадиях обучения стоимость обучения обычно уменьшается,
  только если увеличивается точность подтверждения,
  поэтому на практике не должно иметь значения то,
  какой критерий использовать.
  
  Использование ранней остановки для определения количества эпох обучения
  
  Как мы уже упоминали в этой главе, ранняя остановка означает,
  что в конце каждой эпохи нам нужно подсчитать
  точность классификации на подтверждающих данных.
  Когда она перестаёт улучшаться, прекращаем работу.
  В итоге установка количества эпох становится простым делом.
  В частности, это означает, что нам не надо специально выяснять,
  как количество эпох зависит от других гиперпараметров.
  Это происходит автоматически.
  Более того, ранняя остановка также автоматически не даёт нам
  переобучиться.
  //// И опять "узелок на память".
  //// Тут надо хорошенько по-разбираться с "эффектом гроккинга".
  //// Не все тут так просто.
  Это, конечно, хорошо, хотя на ранних стадиях экспериментов
  может быть полезно отключить раннюю остановку,
  чтобы вы могли увидеть признаки переобучения
  и использовать их для подстройки подхода к регуляризации.
  
  Для реализации РО нам нужно более конкретно описать,
  что означает <остановка улучшения точности классификации>.
  Как мы видели, точность может очень сильно прыгать туда и сюда,
  даже когда общая тенденция улучшается.
  Если мы остановимся в первый раз, когда точность уменьшится,
  то мы почти наверняка не дойдём до возможных дальнейших улучшений.
  Подход лучше - прекратить обучение,
  если наилучшая точность классификации не улучшается достаточно долгое время.
  Допустим, к примеру, что мы занимаемся MNIST.
  Тогда мы можем решить прекратить процесс,
  если точность классификации не улучшалась за последние десять эпох.
  Это гарантирует, что мы не остановимся слишком рано,
  из-за неудачи при обучении,
  но и не будем ждать вечно каких-то улучшений,
  которые так и не произойдут.
  //// Нет, тут нужно какое-то другое обоснование,
  //// ведь можно продолжить обучение, что-то изменив,
  //// например, в той же регуляризации или функции стоимости/ошибки.
  
  Это правило <без улучшений за десять эпох>
  хорошо подходит для первичного исследования MNIST.
  Однако сети иногда могут выйти на плато
  близ определённой точности классификации,
  остаться там довольно долгое время,
  а потом снова начать улучшаться.
  //// Тот самый "эффект гроккинга".
  Если вам нужно достичь очень хорошей эффективности,
  то правило <без улучшений за десять эпох>
  может быть для этого слишком агрессивным.
  Поэтому я рекомендую использовать правило <без улучшений за десять эпох>
  для первичных экспериментов,
  и постепенно принимает более мягкие правила,
  когда вы начнёте лучше понимать поведение вашей сети:
  <без улучшений за двадцать эпох>, <без улучшений за пятьдесят эпох>,
  и так далее.
  Конечно, это даёт нам ещё один гиперпараметр для оптимизации!
  Но на практике этот гиперпараметр обычно легко настроить
  для получения хороших результатов.
  А для задач, отличных от MNIST,
  правило <без улучшений за десять эпох> может оказаться слишком агрессивным,
  или недостаточно агрессивным,
  в зависимости от частностей конкретной задачи.
  Однако немного поэкспериментировав,
  обычно довольно легко найти подходящую стратегию ранней остановки.
  
  Мы пока не использовали раннюю остановку в наших экспериментах с MNIST.
  Всё из-за того, что мы проводили много сравнений различных подходов к обучению.
  Для таких сравнений полезно использовать одинаковое количество эпох во всех случаях.
  Однако стоит изменить network2.py, введя в программу РО.
  
  Задачи
  
  Измените network2.py так, чтобы там появилась РО
  по правилу <без изменений за n эпох>, где n - настраиваемый параметр.
  
  Придумайте правило ранней остановки,
  отличное от <без изменений за n эпох>.
  В идеале, правило должно искать компромисс
  между получением точности с высоким подтверждением
  и достаточно небольшим временем обучения.
  Добавьте правило в network2.py,
  и запустите три эксперимента по сравнению точностей подтверждения
  и количества эпох обучения с правилом <без изменений за 10 эпох>.
  
  План изменения скорости обучения
  
  Пока мы держали скорость обучения ? постоянной.
  Однако часто бывает полезно изменять её.
  На ранних этапах процесса обучения
  веса с большой вероятностью будут назначены совершенно неправильно.
  Поэтому лучше будет использовать большую скорость обучения,
  которая заставит веса меняться побыстрее.
  Затем можно уменьшить скорость обучения,
  чтобы сделать более тонкую подстройку весов.
  
  Как нам наметить план изменения скорости обучения?
  Тут можно применять множество подходов.
  Один естественный вариант - использовать ту же базовую идею, что и в РО.
  Мы удерживаем скорость обучения постоянной,
  пока точность подтверждения не начнёт ухудшаться.
  Затем уменьшаем СО на некоторую величину,
  допустим, в два или в десять раз.
  Повторяем это много раз,
  до тех пор, пока СО не окажется в 1024 (или в 1000) раз меньше начальной.
  И заканчиваем обучение.
  
  План изменения скорости обучения может улучшить эффективность,
  а также открывает огромные возможности по выбору плана.
  И это может стать головной болью
  - можно целую вечность провести за оптимизацией плана.
  Для первых экспериментов я бы предложил использовать одно,
  постоянное значение СО.
  Это даст вам хорошее первое приближение.
  Позже, если вы захотите выжать из сети наилучшую эффективность,
  стоит поэкспериментировать с планом изменения скорости обучения
  так, как я это описал.
  В достаточно легко читаемой научной работе 2010 года
  http://arxiv.org/abs/1003.0358
  демонстрируются преимущества переменных скоростей обучения при атаке на MNIST.
  
  Упражнение
  
  Измените network2.py так,
  чтобы она реализовывала следующий план изменения скорости обучения:
  уполовинить СО каждый раз,
  когда точность подтверждения удовлетворяет правилу <без изменений за 10 эпох>,
  и прекратить обучение, когда скорость обучения упадёт до 1/128 от начальной.
  
  Параметр регуляризации ?
  
  Я рекомендую начинать вообще без регуляризации (?=0,0),
  //// А почему? Хотя у меня примерно схожие предположения/рекомендации,
  //// но совершенно по другим причинам.
  и определить значение ?, как указано выше.
  Используя выбранное значение ?,
  мы затем можем использовать подтверждающие данные
  для выбора хорошего значения ?.
  Начите с ?=1,0
  (хорошего аргумента в пользу именно такого выбора у меня нет),
  а потом увеличивайте или уменьшайте его в 10 раз,
  чтобы повышать эффективность в работе с подтверждающими данными.
  Найдя правильный порядок величины,
  можно подстраивать значение ? более точно.
  После этого нужно снова вернуться к оптимизации ?.
  
  Упражнение
  
  Возникает искушение использовать градиентный спуск,
  чтобы попытаться выучить хорошие значения для таких гиперпараметров,
  как ? и ?.
  Можете ли вы догадаться о препятствии,
  не дающем использовать градиентный спуск для определения ??
  Можете ли вы догадаться о препятствии,
  не дающем использовать градиентный спуск для определения ??
  //// Честно признаюсь, я не догадался.
  
  Как я выбрал гиперпараметры в начале книги
  
  Если вы используете рекомендации из этого раздела,
  вы увидите, что подобранные значения ? и ?
  не всегда точно соответствуют тем, что я использовал ранее.
  Просто у книги есть ограничения по тексту,
  из-за которых иногда было непрактично заниматься оптимизацией гиперпараметров.
  Вспомните обо всех сравнениях разных подходов к обучению,
  которыми мы занимались
  - сравнения квадратичной функции стоимости и перекрёстной энтропии,
  старых и новых методов инициализации весов,
  запуски с и без регуляризации, и так далее.
  Чтобы эти сравнения были осмысленными,
  я пытался не менять гиперпараметры между сравниваемыми подходами
  (или правильно их масштабировать).
  Конечно, нет причин для того, чтобы одни и те же гиперпараметры
  были оптимальными для всех разных подходов к обучению,
  поэтому используемые мною гиперпараметры были результатом компромисса.
  
  В качестве альтернативы я бы мог попытаться
  оптимизировать все гиперпараметры для каждого подхода к обучению по максимуму.
  Это был бы более хороший и честный подход,
  поскольку мы бы взяли всё самое лучшее от каждого подхода к обучению.
  Однако мы провели десятки сравнений,
  и на практике это было бы слишком затратно с вычислительной точки зрения.
  Поэтому я решил остановиться на компромиссе,
  использовать достаточно хорошие
  (но не обязательно оптимальные)
  варианты гиперпараметров.
  
  Размер мини-пакета
  
  Как нужно выбирать размер мини-пакета?
  Для ответа на этот вопрос, давайте сначала предположим,
  что мы занимаемся онлайн-обучением, то есть, используем мини-пакет размера 1.
  
  Очевидная проблема с онлайн-обучением состоит в том,
  что использование мини-пакетов, состоящих из единственного обучающего примера,
  приведёт к серьёзным ошибкам в оценке градиента.
  Но на самом деле эти ошибки не представят такой уж серьёзной проблемы.
  Причина в том, что отдельные оценки градиента не должны быть сверхточными.
  Нам лишь нужно получить достаточно точную оценку для того,
  чтобы наша функция стоимости снижалась.
  Это, как если бы вы пытались попасть на северный магнитный полюс,
  но у вас был бы ненадёжный компас,
  при каждом измерении ошибающийся на 10-20 градусов.
  Если вы будете достаточно часто сверяться с компасом,
  и в среднем он будет указывать верное направление,
  вы в итоге сможете добраться до северного магнитного полюса.
  
  Если учитывать этот аргумент,
  кажется, что нам стоит использовать онлайн-обучение.
  Но на самом деле ситуация оказывается несколько более сложной.
  В задаче к последней главе я указал,
  что для вычисления обновления градиента
  для всех примеров в мини-пакете одновременно
  можно использовать матричные техники, а не цикл.
  В зависимости от подробностей вашего железа
  и библиотеки для линейной алгебры
  может оказаться гораздо быстрее вычислять оценку для мини-пакета
  размером, допустим, 100,
  чем вычислять оценку градиента для мини-пакета в цикле
  для 100 обучающих примеров.
  Это может оказаться, допустим, всего в 50 раз медленнее, а не в 100.
  
  Сперва кажется, что это нам не сильно помогает.
  С размером мини-пакета в 100 правило обучения для весов выглядит, как:
  
  $ w \rightarrow w' = w-\eta \frac{1}{100} \sum_x \nabla C_x \tag{100} $
  
  где суммирование идёт по обучающим примерам в мини-пакете. Сравните с
  
  $ w \rightarrow w' = w-\eta \nabla C_x \tag{101} $
  
  для онлайн-обучения.
  
  Даже если на обновление мини-пакета уйдёт в 50 раз больше времени,
  всё равно онлайн-обучение кажется лучшим вариантом,
  поскольку обновляться мы будем гораздо чаще.
  Но допустим, однако, что в случае с мини-пакетом
  мы увеличили скорость обучения в 100 раз,
  тогда правило обновления превращается в:
  
  $ w \rightarrow w' = w-\eta \sum_x \nabla C_x \tag{102} $
  
  Это похоже на 100 отдельных ступеней онлайн-обучения со скоростью обучения ?.
  Однако на одну ступень онлайн-обучения уходит всего в 50 раз больше времени.
  Конечно, на самом деле это не ровно 100 ступеней онлайн-обучения,
  поскольку в мини-пакете все ?Cx оцениваются для одного и того же набора весов,
  в отличие от кумулятивного обучения, происходящего в онлайн-случае.
  И всё же кажется, что использование более крупных мини-пакетов ускорит процесс.
  //// И тут хотелось бы по-больше экспериментальных данных это во-первых.
  //// А во-вторых, я просто убежден, что обучение на последовательных
  //// этапах обучения "троганья с места", "инфляции", "компрессии",
  //// "стабилизации" и "стагнации" должны быть различным.
  //// Соответственно, и размер батча должен быть разным на разных этапах.
  //// Но это лучше рассмотреть отдельно.
  
  Учитывая все эти факторы, выбор лучшего размера мини-пакета - это компромисс.
  //// В технике "компромисс" часто следствие не найденного хорошего решения.
  Выберете слишком маленький,
  и не получите всё преимущество хороших матричных библиотек,
  оптимизированных для быстрого железа.
  Выберете слишком большой,
  и не будете обновлять веса достаточно часто.
  Вам необходимо выбрать компромиссное значение,
  максимизирующее скорость обучения.
  К счастью, выбор размера мини-пакета,
  при котором скорость максимизируется,
  относительно независима от других гиперпараметров
  (кроме общей архитектуры),
  поэтому, чтобы найти хороший размер мини-пакета,
  не обязательно их оптимизировать.
  Следовательно, достаточно будет использовать приемлемые
  (не обязательно оптимальные) значения у других гиперпараметров,
  а потом попробовать несколько разных размеров мини-пакетов,
  масштабируя ?, как указано выше.
  Постройте график зависимости точности подтверждения от времени
  (реального прошедшего времени, не эпох!),
  и выбирайте размер мини-пакета,
  дающий наиболее быстрое улучшение быстродействия.
  С выбранным размером мини-пакета можете перейти
  к оптимизации других гиперпараметров.
  
  Конечно, как вы уже, без сомнения, поняли,
  в нашей работе такой оптимизации я не проводил.
  В нашей реализации НС вообще не используется
  быстрый подход к обновлению мини-пакетов.
  Я просто использовал размер мини-пакета 10,
  не комментируя и не объясняя его, почти во всех примерах.
  А вообще мы могли бы ускорить обучение, уменьшив размер мини-пакета.
  Я этого не делал, в частности, потому,
  что мои предварительные эксперименты предполагали,
  что ускорение будет довольно скромным.
  Но в практических реализациях нам бы однозначно хотелось реализовать
  наиболее быстрый подход к обновлениям мини-пакетов,
  и попытаться оптимизировать их размер,
  чтобы максимизировать общую скорость.
  
  Автоматизированные техники
  
  Я описывал эти эвристические подходы, как нечто,
  что нужно подстраивать руками.
  Ручная оптимизация - хороший способ получить представление о работе НС.
  Однако, и, впрочем, неудивительно,
  огромная работа уже проделана над автоматизацией этого проекта.
  Распространённой техникой является поиск по решётке,
  систематически просеивающий решётку в пространстве гиперпараметров.
  Обзор достижений и ограничений этой техники
  (а также рекомендации по легко реализуемым альтернативам)
  можно найти в работе 2012 года.
  http://dl.acm.org/citation.cfm?id=2188395
  Было предложено множество сложных техник.
  Не буду делать обзор их всех,
  но хочу отметить многообещающую работу 2012 года,
  http://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf
  использовавшую байесовскую оптимизацию гиперпараметров.
  Код из работы открыт всем,
  и с определённым успехом использовался и другими исследователями.
  //// Термин "определённый успех" рождает "определенный оптимизм"
  //// и, одновременно, "определенный скепсис".
  
  Суммируем
  
  Пользуясь практическими правилами, которые я описал,
  вы не получите от своей НС совершенно наилучшие результаты из всех возможных.
  Но они, вероятно, обеспечат вам хорошую отправную точку
  и фундамент для дальнейших улучшений.
  В частности, я в основном описывал гиперпараметры независимо.
  На практике между ними существует связь.
  Вы можете экспериментировать с ?,
  решить, что нашли правильное значение,
  потом начать оптимизировать ?,
  и обнаружить, что это нарушает вашу оптимизацию ?.
  На практике полезно двигаться в разные стороны,
  постепенно приближаясь к хорошим значениям.
  //// Этакий "гипер-мета-градиентный спуск".
  Превыше прочего учтите, что описанные мною эвристические подходы
  - это простые практические правила,
  но не нечто, вырезанное в камне.
  Вам нужно искать признаки того, что что-то не работает,
  и иметь желание экспериментировать.
  В частности, тщательно отслеживать поведение вашей нейросети,
  особенно точность подтверждения.
  
  Сложность выбора гиперпараметров усугубляется тем,
  что практические знания по их выбору
  размазаны по множеству исследовательских работ и программ,
  а часто находятся только в головах отдельных практиков.
  Существует огромное множество работ с описаниями того,
  как следует поступать
  (часто противоречащие друг другу).
  Однако существует несколько особенно полезных работ,
  синтезирующих и выделяющих крупную часть этих знаний.
  В работе Йошуа Бенджио от 2012 года
  http://arxiv.org/abs/1206.5533
  даются практические рекомендации по использованию обратного распространения
  и градиентного спуска для обучения НС,
  включая и глубокие НС.
  Множество деталей Бенджио описывает гораздо подробнее. Чем я,
  включая систематический поиск гиперпараметров.
  Ещё одна хорошая работа - работа 1998 года Йанна Лекуна и др.
  http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf
  Обе работы появляются в чрезвычайно полезной книге 2012 года,
  где собрано множество трюков, часто используемых в НС:
  "Нейросети: хитрости ремесла".
  http://www.springer.com/computer/theoretical+computer+science/book/978-3-642-35288-1
  Книжка дорогая, но многие её статьи были выложены в интернет их авторами,
  и их можно найти в поисковиках.
  
  Из этих статей, и, особенно из собственных экспериментов,
  становится ясно одно:
  задачу оптимизации гиперпараметров нельзя назвать полностью решённой.
  Всегда существует ещё один трюк,
  который можно попробовать для улучшения эффективности.
  У писателей есть поговорка,
  что книгу нельзя закончить, а можно только бросить.
  То же верно и для оптимизации НС:
  пространство гиперпараметров настолько огромно,
  что оптимизацию нельзя закончить, а можно лишь прекратить,
  оставив НС потомкам.
  Так что вашей целью будет разработка рабочего процесса,
  позволяющего вам быстро провести неплохую оптимизацию,
  и при этом оставляющего вам возможность по необходимости
  попробовать более детальные варианты оптимизации.
  
  Трудности с подбором гиперпараметров заставляют некоторых людей жаловаться,
  что НС требуют слишком много усилий по сравнению с другими техниками МО.
  Я слышал много вариантов жалоб типа:
  <Да, хорошо настроенная НС может выдать наилучшую эффективность при решении задачи.
  Но, с другой стороны, я могу попробовать случайный лес
  [или SVM, или любую другую вашу любимую технологию],
  и она просто работает.
  У меня нет времени разбираться с тем, какая НС мне подойдёт>.
  Конечно, с практической точки зрения хорошо иметь под ругой
  лёгкие в применении техники.
  Это особенно хорошо, когда вы только начинаете работать с задачей,
  и ещё непонятно, может ли вообще МО помочь её решить.
  С другой стороны, если вам важно достичь оптимальных результатов,
  вам может понадобиться испытать несколько подходов,
  требующих более специальных знаний.
  Было бы здорово, если бы МО всегда было лёгким делом,
  но не существует причин, по которым оно априори должно быть тривиальным.
  //// Кроме, пожалуй, одной - хорошей, согласующейся с практикой, теорией.
  //// А если вместо "теории" набор "полуэвристических/полушаманских" практик,
  //// то да, ориентиров в блужданиях по многомерному пространству гиперпараметров,
  //// действительно, совсем не много.
  
  Другие техники
  
  Каждая из техник, проработанных в этой главе, ценна сама по себе,
  но это не единственная причина, по которой я их описал.
  Более важно ознакомиться с некоторыми из проблем,
  которые могут встретиться в области НС,
  и со стилем анализа, который может помочь их преодолеть.
  В каком-то смысле, мы учимся тому, как размышлять о НС.
  В оставшейся части главы я кратко опишу набор других техник.
  Их описания будут не такими глубокими, как у предыдущих,
  но должны передать некие ощущения, касающиеся разнообразия техник,
  встречающихся в области НС.
  
  Вариации стохастического градиентного спуска
  
  Стохастический градиентный спуск через обратное распространение
  хорошо послужил нам во время атаки на задачу классификации рукописных цифр из MNIST.
  Однако к оптимизации функции стоимости есть множество других подходов,
  и иногда они показывают эффективность,
  превосходящую таковую у стохастического градиентного спуска с мини-пакетами.
  В данной секции я кратко опишу два таких подхода,
  гессиан и импульс.
  
  Гессиан
  
  Для начала давайте отложим в сторону НС.
  Вместо этого просто рассмотрим абстрактную проблему
  минимизации функции стоимости C от многих переменных, w=w1,w2,...,
  то есть, C=C(w).
  По теореме Тейлора функцию стоимости в точке w можно аппроксимировать:
  
  $ C(w+\Delta w) = C(w) + \sum_j \frac{\partial C}{\partial w_j} \Delta w_j \nonumber \\ + \frac{1}{2} \sum_{jk} \Delta w_j \frac{\partial^2 C}{\partial w_j \partial w_k} \Delta w_k + \ldots \tag{103} $
  
  Мы можем переписать это компактнее, как
  
  $ C(w+\Delta w) = C(w) + \nabla C \cdot \Delta w + \frac{1}{2} \Delta w^T H \Delta w + \ldots \tag{104} $
  
  где ?C - обычный вектор градиента,
  а H - матрица, известная, как матрица Гессе,
  на месте jk в которой находится ?2C/?wj?wk. //// Используются вторые производные.
  Допустим, мы аппроксимируем C,
  отказавшись от членов высшего порядка,
  скрывающихся за многоточием в формуле:
  
  $ C(w+\Delta w) \approx C(w) + \nabla C \cdot \Delta w + \frac{1}{2} \Delta w^T H \Delta w \tag{105} $
  
  Используя алгебру, можно показать,
  что выражение с правой стороны можно минимизировать, выбрав:
  
  $ \Delta w = -H^{-1} \nabla C \tag{106} $
  
  Строго говоря, для того, чтобы это был именно минимум, а не просто экстремум,
  нужно предположить, что матрица Гессе определеннее положительна.
  Интуитивно это значит, что функция С похожа на долину,
  а не на гору или седловину.
  
  Если (105) будет хорошим приближением к функции стоимости,
  стоит ожидать, что переход от точки w к точке w+?w=w?H?1?C
  должен значительно уменьшить функцию стоимости.
  Это предлагает возможный алгоритм минимизации стоимости:
  
  Выбрать начальную точку w.
  
  Обновить w до новой точки, w?=w?H?1?C, где гессиан H и ?C вычисляются в w.
  
  Обновить w' до новой точки, w??=w??H??1??C, где гессиан H и ?C вычисляются в w'.
  
  ......
  
  
  На практике, (105) является лишь приближением, и лучше брать шаги поменьше.
  Мы сделаем это, постоянно обновляя w на величину ?w=??H?1?C,
  где ? - это скорость обучения.
  
  Такой подход к минимизации функции стоимости известен, как оптимизация Гессе.
  Существуют теоретические и эмпирические результаты,
  показывающие, что методы Гессе сходятся к минимуму за меньшее количество шагов,
  чем стандартный градиентный спуск.
  В частности, посредством включения информации об изменениях второго порядка
  в функции стоимости,
  в подходе Гессе возможно избежать многих патологий,
  встречающихся в градиентном спуске.
  Более того, существуют версии алгоритма обратного распространения,
  которые можно использовать для вычисления гессиана.
  
  Если оптимизация Гессе такая классная,
  что же мы не используем её в наших НС?
  К сожалению, хотя у неё много желанных свойств,
  есть и одно очень нежелательное:
  её очень сложно применять на практике.
  Часть проблемы - огромный размер матрицы Гессе.
  Допустим, у нас есть НС с 10**7 весов и смещений.
  Тогда в соответствующей матрице Гессе будет 10**7 * 10**7 = 10**14 элементов.
  Слишком много!
  В итоге вычислять H?1?C на практике получается очень сложно.
  Но это не значит, что о ней бесполезно знать.
  Множество вариантов градиентного спуска вдохновлено оптимизацией Гессе,
  они просто избегают проблемы чрезмерно больших матриц.
  Давайте взглянем на одну такую технику,
  градиентный спуск на основе импульса.
  
  Градиентный спуск на основе импульса
  
  Интуитивно, преимущество оптимизации Гессе состоит в том,
  что она включает не только информацию о градиенте,
  но и информацию о его изменении.
  Градиентный спуск на основе импульса основан на схожей интуиции,
  однако избегает крупных матриц из вторых производных.
  Чтобы понять технику импульса,
  вспомним нашу первую картинку градиентного спуска,
  в которой мы рассматривали шар, скатывающийся в долину.
  Тогда мы увидели, что градиентный спуск, вопреки своему имени,
  лишь немного напоминает шар, падающий на дно.
  Техника импульса изменяет градиентный спуск в двух местах,
  что делает его более похожим на физическую картину.
  Во-первых, она вводит понятие <скорости> для параметров,
  которые мы пытаемся оптимизировать.
  Градиент пытается изменить скорость,
  а не <местоположение> напрямую,
  похоже на то, как физические силы меняют скорость,
  и лишь косвенно влияют на местоположение.
  Во-вторых, метод импульса представляет нечто вроде члена для трения,
  который постепенно уменьшает скорость.
  
  Давайте дадим более математически точное определение.
  Введём переменные скорости v=v1,v2,...,
  по одной на каждую соответствующую переменную wj
  (в нейросети эти переменные, естественно, включают все веса и смещения).
  Затем мы меняем правило обновления градиентного спуска ww? = w???C на
  
  $ v \rightarrow v' = \mu v - \eta \nabla C \tag{107} $
  
  $ w \rightarrow w' = w+v' \tag{108} $
  
  В уравнениях ? - это гиперпараметр, управляющий количеством торможения,
  или трения системы.
  Чтобы понять смысл уравнений, сначала полезно рассмотреть случай,
  в котором ?=1, то есть, когда трение отсутствует.
  В таком случае изучение уравнений показывает,
  что теперь <сила> ?C меняет скорость v,
  а скорость управляет скоростью изменения w.
  Интуитивно, скорость можно набрать,
  постоянно добавляя к ней члены градиента.
  Это значит, что если градиент движется примерно в одном направлении
  в течение нескольких этапов обучения,
  мы можем набрать достаточно большую скорость движения в этом направлении.
  Представьте, к примеру, что происходит при движении вниз по склону:
  
  .....
  
  С каждым шагом вниз по склону скорость увеличивается,
  и мы движемся всё быстрее и быстрее ко дну долины.
  Это позволяет технике скорости работать гораздо быстрее,
  чем стандартному градиентному спуску.
  Конечно, проблема в том, что, достигнув дна долины, мы его проскочим.
  Или, если градиент будет меняться слишком быстро,
  могло бы оказаться, что мы движемся в обратном направлении.
  В этом и смысл введения гиперпараметра ? в (107).
  Ранее я сказал, что ? управляет количеством трения в системе;
  точнее, количество трения нужно представлять себе в виде 1-?.
  Когда ?=1, как мы увидели, трения нет,
  и скорость полностью определяется градиентом ?C.
  И наоборот, когда ?=0, трения очень много, скорость не набирается,
  и уравнения (107) и (108) сводятся к обычным уравнениям градиентного спуска,
  ww? = w???C.
  На практике, использование значения ? в промежутке между 0 и 1
  может дать нам преимущество возможности набора скорости
  без опасности проскочить минимум.
  Такое значение для ? мы можем выбрать при помощи
  отложенных подтверждающих данных примерно так же,
  как выбирали значения для ? и ?.
  
  Пока я избегал присваивания имени гиперпараметру ?.
  Дело в том, что стандартное имя для ? было выбрано плохо:
  он называется коэффициентом импульса.
  Это может сбить вас с толку,
  поскольку ? совсем не похож не понятие импульса из физики.
  Он куда сильнее связан с трением.
  Однако термин <коэффициент импульса> широко используется,
  поэтому мы тоже продолжим его использовать.
  
  Приятная особенность техники импульса заключается в том,
  что для изменения реализации градиентного спуска
  с целью включить в неё эту технику не требуется почти ничего делать.
  Мы всё ещё можем использовать обратное распространение для подсчёта градиентов,
  как и ранее, и использовать такие идеи,
  как проверка стохастически выбранных мини-пакетов.
  В этом случае мы можем получить некоторые преимущества оптимизации Гессе,
  используя информацию об изменении градиента.
  Однако всё это происходит без недостатков,
  и лишь с небольшими изменениями кода.
  На практике техника импульса широко используется
  и часто помогает ускорять обучение.
  
  Упражнения
  
  Что пойдёт не так, если мы будем использовать ?>1 в технике импульса?
  Что пойдёт не так, если мы будем использовать ?<0 в технике импульса?
  
  Задача
  
  Добавьте стохастический градиентный спуск на основе импульса в network2.py.
  
  Другие подходы к минимизации функции стоимости
  
  Было разработано и множество других подходов к минимизации функции стоимости,
  и по поводу наилучшего подхода согласия не достигнуто.
  Углубляясь в тему нейросетей, полезно покопаться и в других технологиях,
  понять, как они работают, какие у них сильные и слабые стороны,
  и как применять их на практике.
  В упомянутой мною ранее работе вводятся и сравниваются несколько таких техник,
  включая спаренный градиентный спуск и метод BFGS
  (а также изучите близко связанный с ним метод BFGS с ограничением памяти,
  или L-BFGS).
  Ещё одна технология, недавно показавшая многообещающие результаты,
  это ускоренный градиент Нестерова, улучшающая технику импульса.
  Однако для многих задач хорошо работает простой градиентный спуск,
  особенно при использовании импульса,
  поэтому мы будем придерживаться стохастического градиентного спуска до конца книги.
  
  Другие модели искусственного нейрона
  
  Пока что мы создавали наши НС с использованием сигмоидных нейронов.
  В принципе, НС, построенная на сигмоидных нейронах,
  может вычислить любую функцию.
  Но на практике сети, построенные на других моделях нейронов,
  иногда опережают сигмоидные.
  В зависимости от применения, сети, основанные на таких альтернативных моделях,
  могут быстрее обучаться, лучше обобщать на проверочные данные,
  или делать и то, и другое.
  Давайте я упомяну парочку альтернативных моделей нейронов,
  чтобы дать вам представление о некоторых широко применяемых вариантах.
  
  Возможно, простейшей вариацией будет танг-нейрон,
  заменяющий сигмоидную функцию гиперболическим тангенсом.
  Выход танг-нейрона со входом x, вектором весов w и смещением b задаётся, как
  
  $ \tanh(w \cdot x+b) \tag{109} $
  
  где tanh, естественно, гиперболический тангенс.
  Оказывается, что он очень близко связан с сигмоидным нейроном.
  Чтобы это увидеть, вспомним, что tanh определяется, как
  
  $ \tanh(z) \equiv \frac{e^z-e^{-z}}{e^z+e^{-z}} \tag{110} $
  
  Применив немного алгебры, легко видеть, что
  
  $ \sigma(z) = \frac{1+\tanh(z/2)}{2} \tag{111} $
  
  то есть, tanh - это просто масштабирование сигмоиды.
  Графически также можно видеть, что у функции tanh та же форма, что и у сигмоиды:
  
  ......
  
  
  Одно различие между танг-нейронами и сигмоидными нейронами состоит в том,
  что выход первых простирается от -1 до 1, а не от 0 до 1.
  Это значит, что при создании сети на основе танг-нейронов
  вам может понадобиться нормализовать ваши выходы
  (и, в зависимости от деталей применения, возможно, и входы)
  немного не так, как в сигмоидных сетях.
  
  Как и сигмоидные, танг-нейроны, в принципе, могут подсчитать любую функцию
  (хотя с этим и существуют некоторые подвохи),
  размечая входы от -1 до 1.
  Более того, идеи обратного распространения
  и стохастического градиентного спуска точно так же легко применять
  к танг-нейронам, как и к сигмоидным.
  
  Упражнение
  
  Докажите уравнение (111).
  
  Нейрон какого типа стоит использовать в сетях, танг или сигмоидный?
  Ответ, мягко говоря, неочевиден!
  Однако существуют теоретические аргументы
  и некоторые эмпирические свидетельства того,
  что танг-нейроны иногда работают лучше.
  Давайте кратенько пройдёмся по одному из теоретически аргументов
  в пользу танг-нейронов.
  Допустим, мы используем сигмоидные нейроны,
  и все активации в сети будут положительными.
  Рассмотрим веса wl+1jk, входящие для нейрона ?j в слое ?l+1.
  Правила обратного распространения (BP4) говорят нам,
  что связанный с этим градиент будет равен alk?l+1j.
  Поскольку активации положительны,
  знак этого градиента будет таким же, как у ?l+1j.
  Это означает, что если ?l+1j положительна,
  тогда все веса wl+1jk будут уменьшаться во время градиентного спуска,
  а если ?l+1j отрицательна, тогда все веса wl+1jk будут увеличиваться
  во время градиентного спуска.
  Иначе говоря, все веса, связанные с одним и тем же нейроном,
  будут либо увеличиваться, либо уменьшаться совместно.
  А это проблема, поскольку, возможно,
  потребуется увеличивать некоторые веса, уменьшая другие.
  Но это может произойти, только если у некоторых входных активаций
  будут разные знаки.
  Что говорит о необходимости замены сигмоиды другой функцией активации,
  например, гиперболическим тангенсом,
  позволяющей активациям быть как положительными, так и отрицательными.
  И действительно, поскольку tanh симметричен относительно нуля,
  tanh(?z) = ?tanh(z),
  можно ожидать, что, грубо говоря, активации в скрытых слоях
  будут поровну распределены между положительными и отрицательными.
  Это поможет гарантировать отсутствие систематического смещения
  в обновлениях весов в ту или иную сторону.
  
  Насколько серьёзно стоит рассматривать этот аргумент?
  Ведь он эвристический, не даёт строгого доказательства того,
  что танг-нейроны превосходят сигмоидные.
  Возможно, у сигмоидных нейронов есть некие свойства,
  компенсирующие эту проблему?
  И действительно, во многих случаях функция tanh показала
  от минимальных до никаких преимуществ по сравнению с сигмоидой.
  К сожалению, у нас не существует простых и быстро реализуемых способов
  проверить, нейрон какого типа будет обучаться быстрее
  или покажет себя эффективнее в обобщении для какого-то конкретного случая.
  
  Другой вариант сигмоидного нейрона - выпрямленный линейный нейрон,
  или выпрямленная линейная единица [rectified linear unit, ReLU].
  Выход ReLU со входом x, вектором весов w и смещением b задаётся так:
  
  $ \max(0, w \cdot x+b) \tag{112} $
  
  Графически выпрямляющая функция max(0,z) выглядит так:
  
  .....
  
  Такие нейроны, очевидно, сильно отличаются и от сигмоидных, и от танг-нейронов.
  Однако они похожи в том, что тоже могут быть использованы
  для вычисления любой функции, и их можно обучать,
  используя обратное распространение и стохастический градиентный спуск.
  
  Когда нужно использовать ReLU вместо сигмоидных или танг-нейронов?
  В последних работах по распознаванию изображений (1, 2, 3, 4)
  были обнаружены серьёзные преимущества использования ReLU почти по всей сети.
  Однако, как и с танг-нейронами,
  у нас пока нет по-настоящему глубокого понимания того,
  когда именно ReLU будут предпочтительнее,
  и почему.
  //// Очень важно об этом не забвать.
  Чтобы получить некое представление о некоторых проблемах,
  вспомните, что сигмоидные нейроны прекращают обучаться при насыщении,
  то есть, когда выход близок к 0 или 1.
  Как мы уже много раз видели в этой главе,
  проблема состоит в том, что члены ?' уменьшают градиент,
  что замедляет обучение.
  Танг-нейроны страдают от похожих трудностей при насыщении.
  При этом увеличение взвешенного входа на ReLU
  никогда не заставит его насытиться,
  поэтому соответственного замедления обучения не произойдёт.
  С другой стороны, когда взвешенный ввод на ReLU будет отрицательным,
  градиент исчезает,
  и нейрон вообще перестаёт обучаться.
  Это лишь пара из множества проблем,
  делающих нетривиальной задачу понимания того,
  когда и как ReLU ведут себя лучше сигмоидных или танг-нейронов.
  
  Я нарисовал картину неопределённости,
  подчёркивая, что у нас пока нет твёрдой теории выбора функций активации.
  И действительно, эта проблема ещё сложнее, чем я описал,
  поскольку возможных функций активации существует бесконечно много.
  Какая из них даст нам наиболее быстро обучающуюся сеть?
  Какая даст наибольшую точность в тестах?
  Я удивлён, насколько мало было по-настоящему глубоких
  и систематических исследований этих вопросов.
  В идеале у нас должна быть теория,
  подробно говорящая нам о том, как выбирать
  (и, возможно, на лету менять)
  //// "О, тепленькая пошла". Вот только пока не слышно о таких решениях.
  наши функции активации.
  С другой стороны, нас не должно останавливать
  отсутствие полноценной теории!
  У нас уже есть мощные инструменты,
  и с их помощью мы можем достичь значительного прогресса.
  //// "Нам нельзя ждать милостей от природы после того, что мы с ней сделали".
  До конца книги я буду использовать сигмоидные нейроны в качестве основных,
  поскольку они хорошо работают и дают конкретные иллюстрации ключевых идей,
  относящихся к НС.
  Но учитывайте, что те же самые идеи можно применять и к другим нейронам,
  и в этих вариантах есть свои преимущества.
  
  Истории из мира нейросетей
  
  Вопрос:
  какой подход вы применяете при использовании и исследовании техник МО,
  которые поддерживаются практически исключительно эмпирическими наблюдениями,
  а не математикой?
  В какой ситуации вы сталкивались с отказом таких техник?
  
  Ответ:
  вам нужно понять, что наши теоретические инструменты весьма слабы.
  Иногда у нас появляется хорошая математическая интуиция
  по поводу того, почему определённая техника должна сработать.
  Иногда интуиция изменяет нам.
  В итоге вопросы превращаются в следующие:
  насколько хорошо мой метод сработает на этой конкретной задаче,
  и насколько велик набор задач, на котором он хорошо работает?
  
  - Вопросы и ответы с исследователем нейросетей Яном Лекуном
  
  Однажды на конференции по основам квантовой механики,
  я заметил то, что мне показалось забавной речевой привычкой:
  по окончанию доклада вопросы аудитории часто начинались с фразы:
  <Мне очень импонирует ваша точка зрения, но...>.
  Квантовые основы - не совсем моя обычная область,
  и я обратил внимание на такой стиль задавания вопросов потому,
  что на других научных конференциях я практически не встречался с тем,
  чтобы спрашивающий выказывал симпатию к точке зрения докладчика.
  В то время я решил, что преобладание таких вопросов говорило о том,
  что прогресса в квантовых основах было достигнуто довольно мало,
  и люди просто только начинали набирать обороты.
  Позднее я понял, что эта оценка была слишком жёсткой.
  Докладчики боролись с одними из самых сложных проблем,
  с которыми только сталкивались человеческие умы.
  Естественно, что прогресс шёл медленно!
  Однако всё равно существовала ценность в том,
  чтобы услышать новости о мышлении людей, касающемся этой области,
  даже если у них не было практически ничего нового.
  
  В данной книге вы могли заметить <нервный тик>,
  сходный с фразой <мне весьма импонирует>.
  Чтобы объяснить, что мы имеем,
  я часто прибегал к словам вроде <эвристически> или <грубо говоря>,
  за которыми следовало объяснение того или иного явления.
  Эти истории правдоподобны,
  но эмпирические свидетельства часто были весьма поверхностны.
  Если вы изучите исследовательскую литературу,
  то увидите, что истории подобного толка появляются
  во многих исследовательских работах, посвящённых нейросетям,
  часто в компании малого количества поддерживающих их свидетельств.
  Как нам относиться к таким историям?
  
  Во многих областях науки
  - особенно там, где рассматриваются простые явления -
  можно найти очень строгие и надёжные свидетельства весьма общим гипотезам.
  Но в НС есть огромное количество параметров и гиперпараметров,
  и существуют чрезвычайно сложные взаимоотношения между ними.
  В таких невероятно сложных системах неимоверно сложно делать
  надёжные общие заявления.
  Понимание НС во всех их полноте, как квантовые основы,
  испытывает пределы человеческого разума.
  Часто нам приходится обходиться свидетельствами в пользу или против
  нескольких определённых частных случаев общего заявления.
  В итоге эти заявления иногда требуется менять или отказываться от них,
  поскольку появляются новые свидетельства.
  
  Один из подходов к этой ситуации - считать,
  что любая эвристическая история про НС подразумевает некий вызов.
  К примеру, рассмотрим процитированное мною объяснение того,
  почему работает исключение (dropout) из работы 2012 года:
  <Эта техника уменьшает сложную совместную адаптацию нейронов,
  поскольку нейрон не может полагаться на присутствие определённых соседей.
  В итоге ему приходиться обучаться более надёжным признакам,
  которые могут быть полезными в совместной работе
  со многими различными случайными подмножествами нейронов>.
  Богатое и провокационное заявление,
  на базе которого можно построить целую исследовательскую программу,
  в которой нужно будет выяснять,
  в чём оно правдиво, где ошибается, а что требует уточнений и изменений.
  И сейчас действительно существует целая индустрия исследователей,
  изучающих исключение (и множество его вариантов),
  пытающихся понять, как оно работает, и какие ограничения имеет.
  Так и со многими другими эвристическими подходами,
  которые мы обсуждали.
  Каждый из них - не только потенциальное объяснение,
  но и вызов к исследованию и более детальному пониманию.
  
  Конечно, ни у одного человека не хватит времени исследовать
  все эти эвристические объяснения достаточно глубоко.
  У всего сообщества исследователей НС уйдут десятилетия на то,
  чтобы разработать реально мощную теорию обучения НС,
  основанную на доказательствах.
  Значит ли это, что стоит отвергать эвристические объяснения,
  как нестрогие и не имеющие достаточно доказательств?
  Нет!
  Нам нужна такая эвристика, которая будет вдохновлять наше мышление.
  Это похоже на эпоху великих географических открытий:
  ранние исследователи часто действовали (и совершали открытия)
  на основе верований, ошибавшихся серьёзнейшим образом.
  Позднее мы исправляли эти заблуждения, пополняя свои географические знания.
  Когда вы плохо что-то понимаете
  - как понимали исследователи географию, и как сегодня мы понимаем НС -
  важнее смело изучать неизвестное,
  чем быть скрупулёзно правым на каждом шагу ваших рассуждений.
  Поэтому вы должны рассматривать эти истории как полезную инструкцию
  к тому, как размышлять о НС,
  поддерживая здоровую осведомлённость об их ограничениях,
  и тщательно отслеживая надёжность свидетельств в каждом случае.
  Иначе говоря, хорошие истории нужны нам для мотивации и вдохновения,
  а скрупулёзные тщательные расследования
  - для того, чтобы открывать реальные факты.
  
  ........
  
  //// Из комментариев.
  
  evorios
  23 июл 2019 в 00:50
  
  Нужна обученная нейросеть,
  которая сможет подбирать гиперпараметры
  для 100 самых популярных моделей нейросетей.
  На вход можно оставить только субъективную оценку наблюдателя
  о промежуточных результатах поиска (помимо параметров обучаемой сети).
  Вот тогда заживём! =)
  
  ........
  
  //// Конец цитирования.
  
  На мой взгляд это был один из самых полезных разделов в части понимания того,
  "насколько все запущено" в области машинного обучения нейросетей,
  и насколько жизненно необходимы исследовательские работы,
  и практические, и теоретические, для лучшего понимания что же происходит
  с нейросетями в процессе обучения.
  
  К сожалению, если судить по тенденциям в области ML & DS,
  пожелания автора о крайней необходимости такой работы, похоже,
  остались без особого внимания.
  Сейчас даже рекомендации, что нужно самостоятельно подбирать эти гиперпараметры,
  чтобы в первую очередь лучше понимать особенности разрабатываемой модели,
  выглядят как "ветхозаветные" и крайне "немодные".
  Сейчас подбор гиперпараметров рекомендуется делать автоматически,
  используя самые разные стратегии и алгоритмы оптимизации,
  через соответствующие фреймворки, например, такой:
  
  "Это мы юзаем: библиотека Optuna в Python для оптимизации гиперпараметров".
  Автор: badcasedaily1 (artem)
  https://habr.com/ru/companies/otus/articles/801463/.
  27 мар 2024 в 20:45
  
  "Оптимизация гиперпараметров за 5 секунд?".
  Автор: The-Founder-1
  https://habr.com/ru/articles/807605/.
  14 апр 2024 в 16:15
  
  \\\ Традиционный подход к оптимизации гиперпараметров включает в себя
  \\\ grid search и random search,
  \\\ иногда они могут быть неэффективными и времязатратными,
  \\\ особенно когда пространство гиперпараметров велико.
  
  .....
  
  \\\ Математика предоставляет нам один механизм предугадывания.
  \\\ Если есть данные уже после перебора парочки комбинаций
  \\\ - они могут дать информацию о том,
  \\\ куда лучше двигаться в последующем переборе,
  \\\ какую часть сетки выбрать и получить оптимальные наборы гиперпараметров быстрее.
  \\\ Вот тут то и нам поможет байесовский способ оптимизации.
  
  \\\ Достаточно предрассчитывать степень неопределенности
  \\\ и предполагаемой структуры зависимости
  \\\ между гиперпараметрами и "целевой" функцией,
  \\\ чтобы приблизиться к нужным значениям.
  
  ......
  
  \\\ Для начала строится априорная вероятностная модель,
  \\\ где описывается первоначальное представление
  \\\ о связи между гиперпараметрами и целевой функцией.
  \\\ Чем больше данных об оценках целевой функции мы получаем,
  \\\ тем лучше наше знание о пространстве гиперпараметров
  \\\ и его зависимости от целевой функции.
  
  \\\ Грубо говоря, байесовский метод оптимизации
  \\\ - модернизированный рандомный поиск.
  
  .....
  
  \\\ Optuna решает проблему оптимизации гиперпараметров,
  \\\ предоставляя легковесный фреймворк
  \\\ для автоматизации поиска оптимальных гиперпараметров.
  \\\ Она использует алгоритмы, такие как TPE, CMA-ES,
  \\\ и даже поддерживает пользовательские алгоритмы.
  
  Очевидно, что эпоха нейросетей подбирающих параметры обучения
  для других нейросетей уже явно "не за горами".
  И вопрос даже не в том, насколько это улучшит качество машинного обучения
  - что-то и где-то явно улучшится и точно упростится.
  А в том, приблизит ли такое "нейросетевое благолепие в квадрате"
  к самому интересному во всей этой теме,
  пониманию того, как обучается и как работает нейросеть на самом деле.
  
  //// Хотя вопрос разработки нейросети, точнее нейросетевого агента,
  //// решающего задачу исследования результатов обучения нейросети,
  //// и на основе этого выстраивающий новую улучшенную тактику/стратегию
  //// обучения изучаемой нейросети, а в перспективе и изменения ее структуры,
  //// меня весьма заинтересовал.
  //// Очень достойная задача для темы "обучения с подкреплением",
  //// это не в "арканоида играть" - тут "думать надо".
  
  
  =========
  
  20.04.2024 19:52
  
  Магия аппроксимации любой функции нейросетью.
  В оглавление.
  
  Считается общеизвестным и строго математически доказанным фактом,
  что нейросеть, даже с одним скрытым слоем, способна аппроксимировать
  любую мыслимую функцию.
  Спорить с математикой просто так "в лоб", безусловно, занятие безнадежное,
  но есть у этой "аксиомы нейронауки" некие, обычно умолчиваемые, "мелкие детали",
  на которые имеет смысл обратить более пристальное внимание.
  Вот с таким прицелом стоит рассмотреть следующею главу прорабатываемого "талмуда":
  
  "Нейросети и глубокое обучение, глава 4:
  визуальное доказательство того, что нейросети способны вычислить любую функцию".
  Автор: SLY_G (Вячеслав Голованов)
  https://habr.com/ru/articles/461659/.
  29 июл 2019 в 10:00
  
  Автор оригинала: Michael Nielsen
  http://neuralnetworksanddeeplearning.com/chap4.html
  
  //// Начало цитирования.
  
  ......
  
  Один из наиболее потрясающих фактов, связанных с нейросетями,
  заключается в том, что они могут вычислить вообще любую функцию.
  То есть, допустим, некто даёт вам какую-то сложную и извилистую функцию f(x):
  
  ......
  
  И вне зависимости от этой функции гарантированно существует такая нейросеть,
  что для любого входа x значение f(x)
  (или некая близкая к нему аппроксимация)
  будет являться выходом этой сети, то есть:
  
  ......
  
  Это работает, даже если это функция многих переменных f=f(x1,:,xm),
  и со многими значениями.
  К примеру, вот сеть, вычисляющая функцию с m=3 входами и n=2 выходами:
  
  ......
  
  Этот результат говорит о том, что у нейросетей
  есть определённая универсальность.
  Неважно, какую функцию мы хотим вычислить,
  мы знаем, что существует нейросеть, способная сделать это.
  
  Более того, теорема универсальности выполняется,
  даже если мы ограничим сети единственным слоем
  между входящими и выходящими нейронами
  - т.н. одним скрытым слоем.
  Так что даже сети с очень простой архитектурой
  могут быть чрезвычайно мощными.
  
  Теорема универсальности хорошо знакома людям, использующим нейросети.
  Но хотя это так, понимание этого факта не так широко распространено.
  А большинство объяснений этого слишком технически сложные.
  К примеру, одна из первых работ, доказывающих этот результат,
  http://www.dartmouth.edu/~gvc/Cybenko_MCSS.pdf
  использовала теорему Хана - Банаха,
  теорему представлений Риса и немного анализа Фурье.
  Если вы математик, вам несложно разобраться в этих доказательствах,
  но большинству людей это не так-то просто.
  А жаль, поскольку базовые причины универсальности просты и прекрасны.
  
  В данной главе я даю простое и по большей части визуальное объяснение
  теоремы универсальности.
  Мы шаг за шагом пройдём по лежащим в её основе идеям.
  Вы поймёте, почему нейросети действительно могут вычислить любую функцию.
  Вы поймёте некоторые ограничения этого результата.
  //// А вот это, пожалуй, самое важное.
  //// "Дьявол кроется в деталях". Например, "проклятье переобучения".
  И поймёте, как результат связан с глубокими НС.
  
  Чтобы следить за материалом этой главы,
  не обязательно читать предыдущие.
  Он структурирован в виде самостоятельного эссе.
  Если у вас есть самое базовое представление о НС,
  вы должны суметь понять объяснения.
  Но я буду иногда давать ссылки на предыдущие материалы,
  чтобы помочь заполнить пробелы в знаниях.
  
  Теоремы универсальности часто встречаются в информатике,
  так, что иногда мы даже забываем, насколько они потрясающие.
  Но стоит напоминать себе:
  возможность вычислить любую произвольную функцию поистине удивительна.
  //// "Теоретическая" возможность не равна "практической".
  Практически любой процесс, который вы можете себе представить,
  можно свести к вычислению функции.
  Рассмотрим задачу поиска названия музыкальной композиции
  на основе краткого отрывка.
  Это можно считать вычислением функции.
  Или рассмотрим задачу перевода китайского текста на английский.
  И это можно считать вычислением функции
  (на самом деле, многих функций, поскольку существует
  множество приемлемых вариантов переводов одного текста).
  Или рассмотрим задачу генерации описания сюжета фильма
  и качества актёрской игры на основе файла mp4.
  Это тоже можно рассматривать, как вычисление некоей функции
  (здесь тоже верна ремарка, сделанная по поводу вариантов перевода текста).
  Универсальность означает,
  что в принципе,
  НС могут выполнять все эти задачи, и множество других.
  
  Конечно, только из того, что мы знаем, что существуют НС,
  способные, допустим, переводить с китайского на английский,
  не следует, что у нас есть хорошие техники для создания
  или даже распознавания такой сети.
  Это ограничение также применимо к традиционным теоремам универсальности
  для таких моделей, как Булевы схемы.
  Но, как мы уже видели в этой книге,
  у НС есть мощные алгоритмы для выучивания функций.
  Комбинация алгоритмов обучения и универсальности
  - смесь привлекательная.
  //// Точнее "завлекательная", пока не столкнешься с необходимостью
  //// обеспечить точность "шесть девяток".
  Пока что в книге мы концентрировались на обучающих алгоритмах.
  В данной главе мы сконцентрируемся на универсальности
  и на том, что она означает.
  
  Два подвоха
  
  До того, как объяснить, почему теорема универсальности верна,
  я хочу упомянуть два подвоха,
  содержащихся в неформальном заявлении
  <нейросеть может вычислить любую функцию>.
  
  Во-первых, это не значит,
  что сеть можно использовать для точного подсчёта любой функции.
  Мы лишь можем получить настолько хорошее приближение,
  насколько нам нужно.
  //// В реальности ПОКА все достигнутые "приближения" ограничиваются,
  //// максимум, "тремя девятками". А чаще всего вообще "одной девяткой",
  //// а "вторая девятка" требует колоссальных усилий и десятков неудачных попыток.
  Увеличивая количество скрытых нейронов, мы улучшаем аппроксимацию.
  К примеру, ранее я иллюстрировал сеть, вычисляющую некую функцию f(x)
  с использованием трёх скрытых нейронов.
  Для большинства функций при помощи трёх нейронов
  можно будет получить только низкокачественное приближение.
  Увеличив количество скрытых нейронов (допустим, до пяти),
  мы обычно можем получить улучшенное приближение:
  
  .......
  
  И ещё улучшить ситуацию, увеличивая количество скрытых нейронов и далее.
  
  Чтобы уточнить это утверждение, допустим, нам дали функцию f(x),
  которую мы хотим вычислить с некоей нужной точностью ?>0.
  Есть гарантия, что при использовании достаточного количества скрытых нейронов,
  мы всегда сможем найти НС, выход которой g(x)
  удовлетворяет уравнению |g(x)?f(x)|  Иначе говоря, аппроксимация будет достигнута с нужной точностью
  для любого возможного входного значения.
  //// Проблема в том, что нейросети используют в подавляющем большинстве случаев,
  //// когда "требуемая функция" НЕИЗВЕСТНА, а есть только некоторые ее реализации,
  //// в виде КОНЕЧНОГО набора примеров,
  //// причем без гарантии того, что в этих примерах все "чисто",
  //// т.е. тет некоторого "шума" или случайных "выбросов" или "артефактов".
  
  Второй подвох состоит в том, что функции,
  которые можно аппроксимировать описанным способом,
  принадлежат к непрерывному классу.
  Если функция прерывается,
  то есть, делает внезапные резкие скачки,
  то в общем случае её будет невозможно аппроксимировать при помощи НС.
  //// И это не совсем так. И даже совсем не так.
  //// Как раз разрывность нейросеть может обеспечить,
  //// но вот получить требуемую разрывность методом обратного распространения ошибки
  //// через градиентный спуск, не важно, стандартный, или усложненный,
  //// задачка весьма нетривиальная.
  И это неудивительно, поскольку наши НС вычисляют
  непрерывные функции от входных данных.
  Однако, даже если функция, которую нам очень нужно вычислить, разрывная,
  часто оказывается достаточно непрерывной аппроксимации.
  Если это так, то мы можем использовать НС.
  На практике это ограничение обычно не является важным.
  //// А вот это не факт, так как истинное представление искомой функции
  //// нам неизвестно, и когда поведение обученной нейросети отличается
  //// от наших ожиданий понять причины этого очень трудно, если вообще возможно.
  //// И тогда начинаются "старые песни о главном", то бишь, о "переобучении".
  
  В итоге, более точным утверждением теоремы универсальности будет то,
  что НС с одним скрытым слоем можно использовать
  для аппроксимации любой непрерывной функции с любой желаемой точностью.
  В данной главе мы докажем чуть менее строгую версию этой теоремы,
  используя два скрытых слоя вместо одного.
  В задачах я кратко опишу как это объяснение можно,
  с небольшими изменениями,
  адаптировать к доказательству, использующему только один скрытый слой.
  //// А какая практическая надобность есть в сетях с одним скрытым слоем?
  
  Универсальность с одним входным и одним выходным значением
  
  Чтобы понять, почему теорема универсальности истинна,
  начнём с понимания того, как создать НС,
  аппроксимирующую функцию только с одним входным и одним выходным значением:
  
  ......
  
  Оказывается, что это - суть задачи универсальности.
  Как только мы поймём этот особый случай,
  будет довольно легко расширить его на функции
  со многими входными и выходными значениями.
  
  Чтобы создать понимание того,
  как сконструировать сеть для подсчёта f, начнём с сети,
  содержащей единственный скрытый слой с двумя скрытыми нейронами,
  и с выходным слоем, содержащим один выходной нейрон:
  
  .......
  
  Чтобы представить, как работают компоненты сети,
  сконцентрируемся на верхнем скрытом нейроне.
  На диаграмме в оригинале статьи можно интерактивно менять вес мышью,
  кликнув на , и сразу же увидеть,
  как меняется функция, вычисляемая верхним скрытым нейроном:
  
  .......
  
  Как мы узнали ранее в книге, скрытый нейрон подсчитывает ?(wx+b),
  где ?(z) ? 1/(1+e?z) - сигмоида.
  Пока что мы довольно часто использовали эту алгебраическую форму.
  Однако для доказательства универсальности будет лучше,
  если мы полностью проигнорируем эту алгебру,
  и вместо этого будем манипулировать и наблюдать за формой на графике.
  Это не только поможет лучше почувствовать, что происходит,
  но и даст нам доказательство универсальности,
  применимое к другим функциям активации, кроме сигмоиды.
  
  Строго говоря, избранный мною визуальный подход
  традиционно не считается доказательством.
  Но я считаю, что визуальный подход даёт больше понимания
  истинности итогового результата, чем традиционное доказательство.
  А, конечно, подобное понимание и есть реальная цель доказательства.
  В предлагаемом мною доказательстве изредка будут попадаться пробелы;
  я буду давать разумное, но не всегда строгое визуальное доказательство.
  Если это беспокоит вас, то считайте своей задачей заполнить эти пробелы.
  Однако не теряйте из виду главной цели:
  понять, почему теорема универсальности верна.
  
  Чтобы начать с этим доказательством,
  кликните в оригинальной диаграмме на смещение b и проведите мышью вправо,
  чтобы увеличить его.
  Вы увидите, что с увеличением смещения график двигается влево,
  но не меняет форму.
  
  Затем протяните его влево, чтобы уменьшить смещение.
  Вы увидите, что график двигается вправо, не меняя форму.
  
  Уменьшите вес до 2-3.
  Вы увидите, что с уменьшением веса кривая распрямляется.
  Чтобы кривая не убегала с графика, возможно, придётся подправить смещение.
  
  Наконец, увеличьте вес до значений более 100.
  Кривая будет становиться всё круче, и в итоге приблизится к ступеньке.
  Попробуйте подрегулировать смещение так,
  чтобы её угол находился в районе точки x=0,3.
  На видео ниже показано, что должно получиться:
  
  ......
  
  Мы можем очень сильно упростить наш анализ,
  увеличив вес так, чтобы выход реально был хорошей аппроксимацией
  ступенчатой функции.
  Ниже я построил выход верхнего скрытого нейрона для веса w=999.
  Это статичное изображение:
  
  ......
  
  Со ступенчатыми функциями работать немного проще, чем с типичными сигмоидами.
  Причина в том, что в выходном слое складываются вклады от всех скрытых нейронов.
  Сумму кучки ступенчатых функций анализировать легко,
  а вот рассуждать о том, что происходит при сложении кучи кривых в виде сигмоиды
  - сложнее.
  //// Но именно это нужно понять, чтобы основные идеи этой главы
  //// можно было приложить к пониманию рельных машинного обучения нейросетей.
  //// К сожалению, это важное замечание автора теряется в общем объеме материала,
  //// и дальше, особенно в выводах автор к нему не возвращается.
  Поэтому будет гораздо проще предположить,
  что наши скрытые нейроны выдаёт ступенчатые функции.
  Точнее, мы делаем это, фиксируя вес w на некоем очень большом значении,
  а потом назначая положение ступеньки через смещение.
  Конечно, работа с выходом, как со ступенчатой функцией
  - это приближение, но очень хорошее,
  и пока что мы будем относиться к функции,
  как к истинно ступенчатой.
  Позднее я вернусь к обсуждению влияния отклонений от этого приближения.
  //// Это весьма интересный момент доказательства "всемогущества".
  //// С одной стороны, он опирается на свойство нелинейности функции активации,
  //// что, вроде как, соответствует практике конструирования нейросетей.
  //// А с другой, НЕ соответствует практике обучения нейросетей,
  //// особенно с применением так рекламируемых "техник регуляризации",
  //// которые как раз такой способ использования "нелинейности" функций активаций
  //// принудительно существенно ограничивают.
  //// Это первое, но не последнее замечание, по некоей "нереальности"
  //// данного подхода к интерпретации "всемогущества" нейросетей.
  //// Точнее, некоторого несоответствия описываемого подхода
  //// к реальным процессам, происходящим при обучении нейросетей
  //// "аппроксимации любой мыслимой функции".
  
  На каком значении x находится ступенька?
  Иначе говоря, как положение ступеньки зависит от веса и смещения?
  
  Для ответа на вопрос попытайтесь изменить вес и смещение
  в интерактивной диаграмме.
  Можете ли вы понять, как положение ступеньки зависит от w и b?
  Попрактиковавшись немного, вы сможете убедить себя,
  что её положение пропорционально b и обратно пропорционально w.
  
  На самом деле, ступенька находится на отметке s=-b/w,
  как будет видно, если подстроить вес и смещение к следующим значениям:
  
  .......
  
  Наши жизни сильно упростятся, если мы будем описывать скрытые нейроны
  единственным параметром, s,
  то есть, положением ступеньки, s=-b/w.
  На следующей интерактивной диаграмме можно менять уже просто s:
  
  .......
  
  Как отмечено выше, мы специально назначили весу w на входе
  очень большое значение
  - достаточно большое, чтобы ступенчатая функция стала хорошим приближением.
  И мы легко можем превратить таким образом параметризованный нейрон
  обратно к обычной форме,
  выбрав смещение b=-ws.
  
  Пока что мы концентрировались на выходе только верхнего скрытого нейрона.
  Давайте посмотрим на поведение всей сети.
  Предположим, что скрытые нейроны вычисляют ступенчатые функции,
  заданные параметрами ступенек s1 (верхний нейрон) и s2 (нижний нейрон).
  Их соответствующими выходными весами будут w1 и w2.
  Вот наша сеть:
  
  .......
  
  Справа строится график взвешенного выхода w1a1 + w2a2 скрытого слоя.
  Здесь a1 и a2 - выходы верхнего и нижнего скрытых нейронов, соответственно.
  Они обозначаются через ,
  поскольку их часто называют активациями нейронов.
  
  Кстати, отметим, что выход всей сети равен \sigma(w1a1 + w2a2 + b),
  где b - смещение выходного нейрона.
  Это, очевидно, не то же самое, что взвешенный выход скрытого слоя,
  график которого мы строим.
  Но пока мы сконцентрируемся на взвешенном выходе скрытого слоя,
  и только позднее подумаем, как он связан с выходом всей сети.
  
  Попробуйте на интерактивной диаграмме в оригинале статьи
  увеличивать и уменьшать ступеньку s1 верхнего скрытого нейрона.
  Посмотрите, как это меняет взвешенный выход скрытого слоя.
  Особенно полезно понять, что происходит, когда s1 превышает s2.
  Вы увидите, что график в этих случаях меняет форму,
  поскольку мы переходим от ситуации,
  в которой верхний скрытый нейрон активируется первым,
  к ситуации, в которой нижний скрытый нейрон активируется первым.
  
  Сходным образом попробуйте манипулировать ступенькой s2
  у нижнего скрытого нейрона,
  и посмотрите, как это меняет общий выход скрытых нейронов.
  
  Попробуйте уменьшать и увеличивать выходные веса.
  Заметьте, как это масштабирует вклад от соответствующих скрытых нейронов.
  Что будет, если один из весов сравняется с 0?
  
  Наконец, попробуйте выставить w1 в 0,8, а w2 в -0,8.
  Получится функция <выступа>, с началом в точке s1, концом в точке s2,
  и высотой 0,8.
  К примеру, взвешенный выход может выглядеть так:
  
  ......
  
  Конечно, выступ можно масштабировать до любой высоты.
  Давайте использовать один параметр, h, обозначающий высоту.
  Также для упрощения я избавлюсь от обозначений и .
  
  .......
  
  Попробуйте увеличивать и уменьшать значение h,
  чтобы посмотреть, как меняется высота выступа.
  Попробуйте сделать h отрицательным.
  Попробуйте менять точки ступенек, чтобы понаблюдать,
  как это меняет форму выступа.
  
  Вы увидите, что мы используем наши нейроны не просто, как графические примитивы,
  но и как более привычные программистам единицы
  - нечто вроде инструкции if-then-else в программировании:
  
  if вход >= начало ступеньки:
   добавить 1 к взвешенному выходу
  else:
   добавить 0 к взвешенному выходу
  
  По большей части я буду придерживаться графических обозначений.
  Однако иногда вам будет полезно переключаться на представление if-then-else
  и размышлять о происходящем в этих терминах.
  
  Мы можем использовать наш трюк с появлением выступа,
  склеив две части скрытых нейронов вместе в одной сети:
  
  .......
  
  Здесь я опустил веса, просто записав значения h для каждой пары скрытых нейронов.
  Попробуйте поиграть с обоими значениями h, и понаблюдать,
  как это меняет график.
  Подвигайте выступы, меняя точки ступенек.
  
  В более общем случае эту идею можно использовать
  для получения любого желаемого количества пиков любой высоты.
  В частности, мы можем разделить интервал [0,1]
  на большое количество (N) подынтервалов,
  и использовать N пар скрытых нейронов для получения пиков любой нужной высоты.
  Посмотрим, как это работает для N=5.
  Это уже довольно много нейронов, поэтому я немного ужму представления.
  Извините за сложную диаграмму
  - я бы мог спрятать сложность за дополнительными абстракциями,
  но мне кажется, что стоит немного помучаться со сложностью,
  чтобы лучше почувствовать то, как работают нейросети.
  //// К сожалению, эта иллюстрация может служить для объяснения того,
  //// КАК МОГУТ работать нейросети для обеспечения "всемогущества".
  //// Но далеко не факт, что они самом деле в основном ТАК работают
  //// для аппроксимации НЕмонотонных функций, а, может даже и линейных функций.
  //// Ниже постараюсь это проиллюстрировать,
  //// а в данном месте хочу обратить на такую особенность реализации
  //// в таком подходе - это необходимость целенаправленной и согласованной
  //// подгонки параметров СВЯЗАННОЙ ПАРЫ нейронов,
  //// в то время как при обучении "методом обратного распространения ошибки"
  //// настройка параметров нейронов выполняется НЕЗАВИСИМО.
  //// Это второй и, на мой взгляд, самый важный аргумент в "несоответствии"
  //// указанного подхода в объяснении того,
  //// как реально осуществляется нейросетью аппроксимация произвольной функции.
  //// Т.е. нейросеть действительно МОЖЕТ так работать,
  //// но вот обучить ее параметры для такого режима работы
  //// "обратным распространением ошибки" в общем случае совершенно нереально.
  
  .......
  
  Вы видите, что у нас есть пять пар скрытых нейронов.
  Точки ступенек соответствующих пар располагаются
  на значениях 0,1/5, затем 1/5,2/5, и так далее, вплоть до 4/5,5/5.
  Эти значения фиксированы - мы получаем пять выступов равной ширины на графике.
  
  У каждой пары нейронов есть связанное с нею значение h.
  Помните, что у выходных связей нейронов есть веса h и -h.
  В оригинале статьи на диаграмме можно кликнуть на значения h
  и подвигать их влево-вправо.
  С изменением высоты меняется и график.
  Изменяя выходные веса, мы конструируем итоговую функцию!
  
  На диаграмме можно ещё кликнуть по графику,
  и потаскать высоту ступеньки вверх или вниз.
  При изменении её высоты вы видите,
  как изменяется высота соответствующего h.
  Соответствующим образом меняются выходные веса +h и -h.
  Иначе говоря, мы напрямую манипулируем функцией,
  график которой показан справа,
  и видим эти изменения в значениях h слева.
  Можно ещё зажать клавишу мыши на одном из выступов,
  а потом провести мышью влево или вправо,
  и выступы будут подстраиваться под текущую высоту.
  
  Настало время справиться с задачей.
  
  Вспомним функцию, которую я нарисовал в самом начале главы:
  
  ........
  
  Тогда я не упоминал об этом, но на самом деле она выглядит так:
  
  $ f(x) = 0.2+0.4 x^2+0.3x \sin(15 x) + 0.05 \cos(50 x) \tag{113} $
  
  ......
  
  Она строится для значений x от 0 до 1,
  а значения по оси y варьируются от 0 до 1.
  
  Очевидно, что эта функция нетривиальная.
  И вы должны придумать, как подсчитать её с использованием нейросетей.
  
  В наших нейросетях выше мы анализировали
  взвешенную комбинацию ?jwjaj выхода скрытых нейронов.
  Мы знаем, как получить значительный контроль над этой величиной.
  Но, как я отметил ранее, эта величина не равна выходу сети.
  Выход сети - это ?(?jwjaj + b), где b - смещение выходного нейрона.
  Можем ли мы получить контроль непосредственно над выходом сети?
  
  Решение - разработать такую нейросеть,
  у которой взвешенный выход скрытого слоя задаётся уравнением ??1?f(x),
  где ??1 - обратная функция ?.
  То есть, мы хотим, чтобы взвешенный выход скрытого слоя был таким:
  
  .......
  
  Если это получится, тогда выход всей сети будет хорошей аппроксимацией f(x)
  (смещение выходного нейрона я установил в 0).
  
  Тогда ваша задача - разработать НС, аппроксимирующую целевую функцию,
  показанную выше.
  Чтобы лучше понять происходящее, рекомендую вам решить эту задачу дважды.
  В первый раз в оригинале статьи кликните на график,
  и напрямую подстройте высоты разных выступов.
  Вам довольно легко будет получить хорошее приближение к целевой функции.
  Степень приближения оценивается средним отклонением,
   разницей между целевой функцией и той функцией, которую подсчитывает сеть.
  Ваша задача - привести среднее отклонение к минимальному значению.
  Задача считается выполненной, когда среднее отклонение не превышает 0,40.
  
  .......
  
  Достигнув успеха, нажмите кнопку Reset,
  которая случайным образом поменяет выступы.
  Второй раз не трогайте график, а изменяйте величины h с левой стороны диаграммы,
  пытаясь привести среднее отклонение к величине 0,40 или менее.
  
  И вот, вы нашли все элементы, необходимые для того,
  чтобы сеть приблизительно вычисляла функцию f(x)!
  Аппроксимация получилась грубой,
  но мы легко можем улучшить результат,
  просто увеличив количество пар скрытых нейронов,
  что увеличит количество выступов.
  
  В частности, легко превратить все найденные данные обратно
  в стандартный вид с параметризацией, используемый для НС.
  Позвольте быстро напомнить, как это работает.
  
  У первого слоя все веса имеют большое постоянное значение, к примеру, w=1000.
  
  Смещения скрытых нейронов вычисляются через b=-ws.
  Так что, к примеру, для второго скрытого нейрона s=0,2
  превращается в b=-1000*0,2=-200.
  
  Последний слой весов определяется значениями h.
  Так что, к примеру, значение, выбранное вами для первого h, h= -0,2,
  означает, что выходные веса двух верхних скрытых нейронов
  равны -0,2 и 0,2 соответственно. И так далее, для всего слоя выходных весов.
  
  Наконец, смещение выходного нейрона равно 0.
  
  И это всё: у нас получилось полное описание НС,
  неплохо вычисляющей изначальную целевую функцию.
  И мы понимаем, как улучшить качество аппроксимации,
  улучшая количество скрытых нейронов.
  
  Кроме того, в нашей оригинальной целевой функции
  f(x)=0,2+0,4x2+0,3sin(15x)+0,05cos(50x) нет ничего особенного.
  Подобную процедуру можно было бы использовать
  для любой непрерывной функции на отрезках от [0,1] до [0,1].
  По сути, мы используем нашу однослойную НС
  для построения справочной таблицы по функции.
  И мы можем взять эту идею за основу,
  чтобы получить обобщённое доказательство универсальности.
  
  Функция от многих параметров
  
  Расширим наши результаты на случай множества входящих переменных.
  Звучит сложно, но все нужные нам идеи можно понять
  уже для случая всего с двумя входящими переменными.
  Поэтому рассмотрим случай с двумя входящими переменными.
  
  Начнём с рассмотрения того, что будет, когда у нейрона есть два входа:
  
  ........
  
  У нас есть входы x и y, с соответствующими весами w1 и w2 и смещением b нейрона.
  Установим вес w2 в 0 и поиграемся с первым, w1, и смещением b,
  чтобы посмотреть, как они влияют на выход нейрона:
  
  .......
  
  Как видим, при w2=0 вход y не влияет на выход нейрона.
  Всё происходит так, будто x - единственный вход.
  
  Учитывая это, что, как вы думаете, произойдёт,
  когда мы увеличим вес w1 до w1=100, а w2 оставим 0?
  Если это сразу вам непонятно,
  подумайте немного над этим вопросом.
  Потом посмотрите следующее видео, где показано, что произойдёт:
  
  ......
  
  Как и ранее, с увеличением входного веса выход приближается к форме ступеньки.
  Разница в том, что наша ступенчатая функция теперь расположена
  в трёх измерениях.
  Как и раньше, мы можем передвигать местоположение ступеньки, изменяя смещение.
  Угол будет находиться в точке sx=-b/w1.
  
  Давайте переделаем диаграмму, чтобы параметром было местоположение ступеньки:
  
  ......
  
  Мы предполагаем, что входящий вес у x имеет большое значение
  - я использовал w1=1000 - и вес w2=0.
  Число на нейроне - это положение ступеньки,
  а x над ним напоминает, что мы передвигаем ступеньку по оси x.
  Естественно, вполне возможно получить ступенчатую функцию по оси y,
  сделав входящий вес для y большим (допустим, w2=1000),
  и вес для x равным 0, w1=0:
  
  .......
  
  Число на нейроне, опять-таки, обозначает положение ступеньки,
  а y над ним напоминает, что мы передвигаем ступеньку по оси y.
  Я бы мог напрямую обозначить веса для x и y,
  но не стал, поскольку это замусорило бы диаграмму.
  Но учтите, что маркер y говорит о том, что вес для y большой, а для x равен 0.
  
  Мы можем использовать только что сконструированные нами ступенчатые функции
  для вычисления функции трёхмерного выступа.
  Для этого мы возьмём два нейрона,
  каждый из которых будет вычислять ступенчатую функцию по оси x.
  Затем мы скомбинируем эти ступенчатые функции с весами h и -h,
  где h - желаемая высота выступа.
  Всё это видно на следующей диаграмме:
  
  .......
  
  Попробуйте поменять величину h.
  Посмотрите, как она связана с весами сети.
  И как она меняет высоту функции выступа справа.
  
  Также попытайтесь изменить точку ступеньки,
  величина которой установлена в 0,30 в верхнем скрытом нейроне.
  Посмотрите, как она меняет форму выступа.
  Что будет, если перенести её за точку 0,70, связанную с нижним скрытым нейроном?
  
  Мы узнали, как построить функцию выступа по оси x.
  Естественно, мы легко можем сделать функцию выступа и по оси y,
  используя две ступенчатые функции по оси y.
   Вспомним, что мы можем сделать это, сделав большие веса на входе y,
  и установив вес 0 на входе x.
  И вот, что получится:
  
  ......
  
  Выглядит почти идентично предыдущей сети!
  Единственное видимое изменение
  - маленькие маркеры y на скрытых нейронах.
  Они напоминают нам о том, что выдают ступенчатые функции для y, а не для x,
  поэтому на входе y вес очень большой, а на входе x - нулевой, а не наоборот.
  Как и раньше, я решил не показывать этого непосредственно,
  чтобы не захламлять рисунок.
  
  Посмотрим, что будет, если мы добавим две функции выступа,
  одну по оси x, другую по оси y, обе высотой h:
  
  ......
  
  Для упрощения диаграммы связи с нулевым весом я опустил.
  Пока что я оставил маленькие маркеры x и y на скрытых нейронах,
  чтобы напомнить, в каких направлениях вычисляются функции выступов.
  Позже мы и от них откажемся, поскольку они подразумеваются входящей переменной.
  
  Попробуйте менять параметр h.
  Как видите, из-за этого меняются выходные веса,
   а также веса обеих функций выступа, x и y.
  
  Созданное нами немного похоже на <функцию башни>:
  
  .......
  
  Если мы можем создать такие функции башен,
  то мы можем использовать их для аппроксимации произвольных функций,
  просто добавляя башни различных высот в разных местах:
  //// И опять эта иллюстрация никак не акцентирует того
  //// что такая многопараметрическое "башнестроение" возможно
  //// только при стратегическом ПЛАНИРОВАНИИ того,
  //// как, в каком направлении, следует менять эти параметры.
  //// В то время при стандартном обучении нейросети
  //// "обратным распространением ошибки" в стохастическом градиентном спуске
  //// такое целенаправленное и согласованное подгонка параметров,
  //// практически, НЕРЕАЛЬНО.
  //// При практическом обучении нейросети параметры "подгоняются" ПО-ДРУГОМУ,
  //// и, как следствие, результирующая картинка
  //// будет кардинально отличаться от "многобашенного" рельефа.
  //// Ниже приведены иллюстрации реального "рельефа решений".
  
  .......
  
  Конечно, мы пока ещё не дошли до создания произвольной функции башни.
  Мы пока сконструировали что-то вроде центральной башни высоты 2h
  с окружающим её плато высоты h.
  
  Но мы можем сделать функцию башни.
  Вспомните, что раньше мы показали,
  как нейроны можно использовать для реализации инструкции if-then-else:
  
   if вход >= порог:
   выход 1
   else:
   выход 0
  
  Это был нейрон с одним входом.
  А нам нужно применить сходную идею к комбинированному выходу скрытых нейронов:
  
   if скомбинированный выход скрытых нейронов >= порог:
   выход 1
   else:
   выход 0
  
  Если мы правильно выберем порог - к примеру, 3h/2,
  втиснутый между высотой плато и высотой центральной башни
  - мы сможем раздавить плато до нуля, и оставить только одну башню.
  
  Представляете, как это сделать?
  Попробуйте поэкспериментировать со следующей сетью.
  Теперь мы строим график выхода всей сети,
  а не просто взвешенный выход скрытого слоя.
  Это значит, что мы добавляем член смещения
  к взвешенному выходу от скрытого слоя,
  и применяем сигмоиду.
  Сможете ли вы найти значения для h и b,
  при которых получится башня?
  Если вы застрянете на этом моменте, вот две подсказки:
  (1) чтобы выходящий нейрон продемонстрировал правильное поведение
  в стиле if-then-else,
  нам нужно, чтобы входящие веса (все h или -h) были крупными;
  (2) значение b определяет масштаб порога if-then-else.
  
  .......
  
  С параметрами по умолчанию выход похож
  на расплющенную версию предыдущей диаграммы, с башней и плато.
  Чтобы получить желаемое поведение, нужно увеличить значение h.
  Это даст нам пороговое поведение if-then-else.
  Во-вторых, чтобы правильно задать порог, нужно выбрать b = -3h/2.
  
  Вот как это выглядит для h=10:
  
  .......
  
  Даже для относительно скромных величин h мы получаем неплохую функцию башни.
  И, конечно, мы можем получить сколь угодно красивый результат,
  увеличивая h и дальше, и удерживая смещение на уровне b=-3h/2.
  
  Давайте попробуем склеить вместе две сети,
  чтобы подсчитать две разные функции башен.
  Чтобы соответствующие роли двух подсетей были ясны,
  я поместил их в отдельные прямоугольники:
  каждый из них вычисляет функцию башни при помощи описанной выше техники.
  График справа показывает взвешенный выход второго скрытого слоя,
  то есть, взвешенную комбинацию функций башен.
  //// Но обучение нейросетей идет не по частям/подсетям,
  //// а целиком, и данный сценарий не соответствует реальности.
  //// И если для поставленной автором задачи проиллюстрировать
  //// саму ВОЗМОЖНОСТЬ нейронкой ступеньчатой/"башенной" формы
  //// аппроксимации любой функции, это можно считать приемлемым приемом.
  //// Но вот для объяснения того, как именно нейронки обучаются и реализуют
  //// аппроксимацию любой функцию данный подход вряд ли подойдет.
  //// Т.е. моя основная претензия к этому материалу в том,
  //// что это различие недостаточно четко акцентировано,
  //// и по ходу материала у читателя может сложиться впечатление,
  //// что именно так нейросети и работают при аппроксимации "любой функции".
  
  ......
  
  В частности, видно, что изменяя веса в последнем слое,
  можно менять высоту выходных башен.
  
  Та же идея позволяет вычислять сколько угодно башен.
  Мы можем сделать их сколь угодно тонкими и высокими.
  В итоге мы гарантируем,
  что взвешенный выход второго скрытого слоя аппроксимирует
  любую нужную функцию двух переменных:
  
  ......
  
  В частности, заставив взвешенный выход второго скрытого слоя
  хорошо аппроксимировать ??1?f,
  мы гарантируем, что выход нашей сети будет
  хорошей аппроксимацией желаемой функции f.
  
  А что же насчёт функций многих переменных?
  
  Попробуем взять три переменных, x1,x2,x3.
  Следующую сеть можно использовать для подсчёта функции башни
  в четырёх измерениях?
  
  .......
  
  Здесь x1,x2,x3 обозначают вход сети. s1, t1 и так далее
  - точки ступенек для нейронов -
  то есть, все веса в первом слое большие,
  а смещения назначены так, чтобы точки ступенек равнялись s1, t1, s2,...
  Веса во втором слое чередуются, +h,-h,
  где h - некое очень большое число.
  Выходное смещение равно -5h/2.
  
  Сеть вычисляет функцию, равную 1, при выполнении трёх условий:
  x1 находится между s1 и t1;
  x2 находится между s2 и t2;
  x3 находится между s3 и t3.
  Сеть равна 0 во всех других местах.
  Это такая башня, у которой 1 - небольшой участок пространства входа,
  и 0 - всё остальное.
  
  Склеивая множество таких сетей,
  мы можем получить сколько угодно башен,
  и аппроксимировать произвольную функцию трёх переменных.
  Та же идея работает в m измерений.
  Меняется только выходное смещение (-m+1/2)h,
  чтобы правильно втиснуть нужные значения и убрать плато.
  
  Хорошо, теперь мы знаем, как использовать НС
  для аппроксимации вещественной функции многих переменных.
  Что насчёт векторных функций f(x1,..,xm) ? Rn?
  Конечно, такую функцию можно рассматривать,
  просто как n отдельных вещественных функций f1(x1,:,xm), f2(x1,:,xm),
  и так далее.
  А потом мы просто склеиваем все сети вместе.
  Так что с этим легко разобраться.
  
  Задача
  
  Мы увидели, как использовать нейросети с двумя скрытыми слоями
  для аппроксимации произвольной функции.
  Можете ли вы доказать, что это возможно делать с одним скрытым слоем?
  Подсказка - попробуйте работать с всего двумя выходными переменными,
  и показать, что:
  (a) возможно получить функции ступенек не только по осям x или y,
  но и в произвольном направлении;
  (b) складывая множество конструкций с шага (a),
  возможно аппроксимировать функцию круглой, а не прямоугольной башни;
  c используя круглые башни, возможно аппроксимировать произвольную функцию.
  Шаг c будет проще сделать, используя материал,
  представленный в этой главе немного ниже.
  
  Выход за рамки сигмоидных нейронов
  
  Мы доказали, что сеть, состоящая из сигмоидных нейронов,
  может вычислить любую функцию.
  Вспомним, что в сигмоидном нейроне входы x1,x2,...
  превращаются на выходе в ?(?jwjxj + b),
  где wj - веса, b - смещение, ? - сигмоида.
  
  .......
  
  Что, если мы рассмотрим другой тип нейрона,
  использующий другую функцию активации, s(z):
  
  .......
  
  То есть, мы предположим, что если у нейрона на входе будет x1,x2,...
  веса w1,w2,.. и смещение b,
  то на выходе будет s(?jwjxj + b).
  
  Мы можем использовать эту функцию активации для получения ступенчатой,
  точно так же, как в случае с сигмоидой.
  Попробуйте (в оригинале статьи) на диаграмме задрать вес до, допустим, w=100:
  
  .....
  
  Как и в случае с сигмоидой, из-за этого функция активации сжимается,
  и в итоге превращается в очень хорошую аппроксимацию ступенчатой функции.
  Попробуйте поменять смещение,
  и вы увидите, что мы можем изменить местоположение ступеньки на любое.
  Поэтому мы можем использовать всё те же трюки, что и ранее,
  для вычисления любой желаемой функции.
  
  Какие свойства должны быть у s(z),
  чтобы это сработало?
  Нам нужно предположить, что s(z) хорошо определена при z->-oo и z->oo.
  Эти пределы - это два значения, принимаемых нашей ступенчатой функцией.
  Нам также нужно предположить, что эти пределы отличаются.
  Если бы они не отличались, ступеньки бы не получилось,
  был бы просто плоский график!
  Но если функция активации s(z) удовлетворяет этим свойствам,
  основанные на ней нейроны универсально подходят для вычислений.
  
  Задачи
  
  Ранее в книге мы познакомились с нейроном другого типа
  - выпрямленным линейным нейроном,
  или выпрямленной линейной единицей [rectified linear unit, ReLU].
  Поясните, почему такие нейроны не удовлетворяют условиям,
  необходимым для универсальности.
  Найдите доказательство универсальности, показывающее,
  что ReLU универсально подходят для вычислений.
  //// Не нашел. Значит мало, что понял.
  
  Допустим, мы рассматриваем линейные нейроны, с функцией активации s(z)=z.
  Поясните, почему линейные нейроны не удовлетворяют условиям универсальности.
  Покажите, что такие нейроны нельзя использовать для универсальных вычислений.
  
  Исправляем ступенчатую функцию
  
  Покамест, мы предполагали, что наши нейроны выдают точные ступенчатые функции.
  Это неплохое приближение, но лишь приближение.
  На самом деле существует узкий промежуток отказа,
  показанный на следующем графике,
  где функции ведут себя совсем не так, как ступенчатая:
  
  .......
  
  В этом промежутке отказа данное мною объяснение универсальности не работает.
  //// Тут есть и еще один интересный момент,
  //// возможно связанный с проблемой "переобучения".
  //// При наличии такого рода "узких промежутков", задача обучения нейросети
  //// становится "немонотонной" и, даже, "стохастической",
  //// т.к. устранить такие "промежутки" стандартной техникой обучения,
  //// практически нереально, т.к. это требует обеспечения
  //// на порядок более сложной целенаправленно согласованности
  //// изменения параметров нейронов.
  
  Отказ не такой уж страшный.
  Задавая достаточно большие входные веса,
  мы можем делать эти промежутки сколь угодно малыми.
  Мы можем сделать их гораздо меньшими, чем на графике, невидимыми глазу.
  //// Но они все равно будут и обязательно проявят себя,
  //// как минимум, в "adversarial attacks".
  //// Или при задаче получить точность выше "одной девятки".
  Так что, возможно, нам не стоит волноваться из-за этой проблемы.
  //// "Вода дырочку найдет".
  
  Тем не менее, хотелось бы иметь некий способ её решения.
  
  Оказывается, её легко решить.
  Давайте посмотрим на это решение для вычисляющих функции НС
  со всего одним входом и выходом.
  Те же идеи сработают и для решения проблемы
  с большим количеством входов и выходов.
  
  В частности, допустим, мы хотим,
  чтобы наша сеть вычислила некую функцию f.
  Как и раньше, мы пытаемся сделать это,
  проектируя сеть так, чтобы взвешенный выход скрытого слоя нейронов был ??1?f(x):
  
  ......
  
  Если мы будем делать это, используя описанную выше технику,
  мы заставим скрытые нейроны выдать последовательность функций выступов:
  
  ......
  
  Я, конечно, преувеличил размер промежутков отказа,
  чтобы их было легче увидеть.
  Должно быть ясно, что если мы сложим все эти функции выступов,
  то получим достаточно хорошую аппроксимацию ??1?f(x)
  везде, кроме промежутков отказа.
  
  Но, допустим, что вместо использования только что описанной аппроксимации,
  мы используем набор скрытых нейронов для вычисления аппроксимации
  половины нашей изначальной целевой функции, то есть, ??1?f(x)/2.
  Конечно, это будет выглядеть,
  просто как масштабированная версия последнего графика:
  
  .......
  
  И, допустим, мы заставим ещё один набор скрытых нейронов
  вычислять приближение к ??1?f(x)/2,
  однако у него основания выступов будут сдвинуты на половину их ширины:
  
  ........
  
  Теперь у нас есть два разных приближения для ??1?f(x)/2.
  Если мы сложим две этих аппроксимации,
  то получим общее приближение к ??1?f(x).
  У этого общего приближения всё равно будут неточности
  в небольших промежутках.
  Но проблема будет меньше, чем раньше
  - ведь точки, попадающие в промежутки отказа первой аппроксимации,
  не попадут в промежутки отказа второй аппроксимации.
  Поэтому аппроксимация в этих промежутках окажется примерно в 2 раза лучше.
  //// Вот тут уже не понял. Похоже, или устал,
  //// или это объяснение попало в "зазор"/"промежуток" понимания
  //// моей личной "межушной" нейросети.
  //// В любом случае, вероятность такого согласованного обучения
  //// нескольких пар нейронов стохастическим градиеннтным спуском,
  //// да еще и с регуляризацией, представляется совершенно невероятной.
  
  Мы можем улучшить ситуацию, добавив большое количество, M,
  накладывающихся аппроксимаций функции ??1?f(x)/M.
  Если все промежутки отказа у них будут достаточно узкими,
  любая тока будет находиться лишь в одном из них.
  Если использовать достаточно большое количество накладывающихся аппроксимаций M,
  в итоге получится прекрасное общее приближение.
  
  Заключение
  
  Рассмотренное здесь объяснение универсальности
  определённо нельзя назвать практическим описанием того,
  как подсчитывать функции при помощи нейросетей!
  //// Вот это главное в этом материале,
  //// но кто на такие "восклицательные знаки" обращает внимание?
  В этом смысле оно больше похоже на доказательство
  универсальности логических вентилей NAND и прочего.
  Поэтому я в основном пытался сделать так,
  чтобы эта конструкция была ясной,
  и ей было просто следовать, не оптимизируя её детали.
  Однако попытки оптимизировать эту конструкцию могут стать для вас
  интересным и поучительным упражнением.
  
  Хотя полученный результат нельзя напрямую использовать для создания НС,
  //// Именно потому, что данный результат возможен только
  //// при целенаправленном "подборе параметров",
  //// но никак не методами "машинного обучения".
  //// Вот такой ремарки не хватает в этом материале.
  он важен, поскольку он снимает вопрос вычислимости
  какой-либо определённой функции при помощи НС.
  Ответ на такой вопрос всегда будет положительным.
  Поэтому правильно спрашивать: не вычислима ли какая-либо функция,
  а каков правильный способ её вычисления.
  //// И вопрос: как ДОСТИГНУТЬ этого способа?
  
  Разработанная нами универсальная конструкция использует
  всего два скрытых слоя для вычисления произвольной функции.
  Как мы обсуждали, возможно получить тот же результат
  при помощи единственного скрытого слоя.
  Учитывая это, вы можете задуматься,
  зачем вообще нам нужны глубокие сети,
  то есть, сети с большим количеством скрытых слоёв.
  Не можем ли мы просто заменить эти сети на неглубокие,
  имеющие один скрытый слой?
  
  Хотя, в принципе, это возможно,
  существуют хорошие практические причины для использования глубоких нейросетей.
  Как описано в главе 1, у глубоких НС есть иерархическая структура,
  позволяющая им хорошо адаптироваться для изучения иерархических знаний,
  которые оказываются полезными для решения реальных проблем.
  Более конкретно, при решении таких задач, как распознавание образов,
  полезно бывает использовать систему,
  понимающую не только отдельные пиксели,
  но и всё более сложные концепции:
  от границ до простых геометрических фигур,
  и далее, вплоть до сложных сцен с участием нескольких объектов.
  В более поздних главах мы увидим свидетельства,
  говорящие в пользу того, что глубокие НС смогут лучше неглубоких
  справиться с изучением подобных иерархий знания.
  Подытоживая:
  универсальность говорит нам, что НС могут подсчитать любую функцию;
  эмпирические свидетельства говорят о том,
  что глубокие НС лучше адаптированы к изучениям функций,
  полезных для решения многих задач реального мира.
  
  .........
  
  //// Из комментариев.
  
  mst_72
  29 июл 2019 в 10:43
  
  Человек на полном серьезе написал,
  что механизм аппроксимации (нейросеть)
  позволяет аппроксимировать (приблизительно вычислить) аппроксимируемую функцию?
  
  Гениально:
  С нетерпением жду статьи про ряды
  
  trofimovep
  29 июл 2019 в 11:47
  
  Если я правильно помню,
  это упрощенная идея доказательства доказательство теоремы Колмогорова-Арнольда,
  о представимости функции от нескольких переменных функцией одной переменной
  
  MarlboroMan
  29 июл 2019 в 12:01
  
  ОК, пусть вычислит md5()
  
  usdglander
  29 июл 2019 в 16:46
  
  Да не вопрос!
  У вас же есть память для хранения 2^128 нейронов в скрытом слое? :)
  
  Kilorad
  30 июл 2019 в 12:02
  
  И ещё датасет нужен соответствующий) Каждая точка
  
  pavlushk0
  29 июл 2019 в 12:42
  
  Всё-таки вычислить или апроксимировать?
  Это вроде сильно не одно и тоже.
  И речь наверно о любых "вычислимых" функциях.
  
  SLY_G
  29 июл 2019 в 16:49
  
  Во-первых, это не значит, что сеть можно использовать
  для точного подсчёта любой функции.
  Мы лишь можем получить настолько хорошее приближение,
  насколько нам нужно
  //// А вот с этим не согласен. Если функция неизвестна,
  //// то неизвестно какая должна быть сеть и,
  //// что очень важно обучающий набор примеров,
  //// чтобы получить "нужное приближение".
  
  DesertFlow
  29 июл 2019 в 14:02
  
  Мне больше нравится объяснение,
  что для каждой возможной комбинации входных параметров
  можно все выходы сделать 0, кроме одного,
  и тогда нейросеть превращается в обычную табличную функцию.
  Очевидно, что при бесконечном числе нейронов даже в одном скрытом слое,
  таким образом можно аппроксимировать любую функцию
  с любой необходимой точностью.
  Просто берём нужное значение из нужной ячейки таблицы, и все.
  Ну а многослойность нужна,
  чтобы уменьшить число нейронов для решения задачи.
  //// Хорошее предложение, вот только нужен еще и соответствующий
  //// метод обучения - "обратное распространение" с градиентным спуском
  //// для такого решения не подойдет.
  
  Kilorad
  29 июл 2019 в 16:03
  
  Автор не упомянул (насколько я понял) ещё одно важное ограничение.
  При обучении нейросети мы должны иметь в датасете точки,
  достаточно близкие к той точке, которую мы собрались аппроксимировать.
  А что значит <достаточно близкие>,
  зависит от вида целевой функции и от вида активационной.
  
  Например, если наша целевая функция - это 2^x,
  у нас есть в датасете неограниченное число точек из интервала (0,1),
  а активационная функция - это сигмоида,
  то совершенно не факт, что нейронка правильно апроксимирует точку с x=5.
  
  Spin7ion
  29 июл 2019 в 23:26
  
  Если вы не против, немного перефразирую.
  Для того, чтобы достичь требуемой точности НС на интервале,
  необходимо на этом интервале вычислить целевую функцию
  и обучить сеть по этим вычислениям.
  
  Тут напрашивается и такой вопрос:
  имея возможность вычислить значение некоторой функции
  (может и приблизительно),
  на сколько разумно использовать НС
  и начиная с какого момента НС будет менее производительна,
  чем более традиционные методы?
  
  Deosis
  30 июл 2019 в 07:50
  
  НС разумно использовать,
  когда традиционное вычисление функции очень дорого.
  
  Spin7ion
  30 июл 2019 в 10:27
  
  Это-то как раз понятно,
  но хочется понять до построения НС, что будет дороже.
  
  Приведу пример:
  есть алгебраическое матричное уравнение риккати.
  Решать его в real time необходимо для SDC регуляторов,
  но не очень приятно, а порой и невозможно
  (зависит от размерности системы).
  Аналитического решения этого уравнения нет и решают его апроксимацией.
  
  Так вот вопрос, сумеет ли НС дать прирост в производительности?
  
  Kilorad
  30 июл 2019 в 09:58
  
  Часто бывает, что НС аппроксимирует эмпирическую функцию.
  Например, каков будет результат общевойскового боя при заданных вводных.
  Соответственно, мы можем или выяснить это экспериментально,
  или иметь некую теорию, позволяющую предсказывать результаты сражений.
  Обучение нейросети - это как раз и есть создание теории из опытных данных.
  
  В общем, если у нас есть удовлетворительная матмодель процесса
  - тогда лучше использовать её.
  Если нет - можно или вывести теорию
  (хорошо, но требует кучу человекочасов),
  или обучить нейросеть
  (плохо для интерпретации, зато автоматически и довольно точно)
  
  napa3um
  2 авг 2019 в 05:49
  
  Без модели исследуемого процесса не обойтись и в случае применения нейросетей,
  волшебным образом она модель не построит,
  она всего лишь инструмент для подбора свободных параметров в моделях,
  построенных людьми.
  Итого вариантов два:
  либо построить модель решения,
  либо модель поиска этого решения
  - подбора свободных параметров первой модели.
  Во втором варианте есть возможность воспользоваться нейросетями.
  
  Kilorad
  2 авг 2019 в 13:44
  
  В смысле <волшебным образом не построит модель>?
  Нафиг тогда вообще нужна нейронка?)
  
  Если у меня есть соображения, как должна выглядеть модель,
  я возьму линейную или полиномиальную регрессию,
  или какую-то ещё известного вида.
  Если вообще никаких идей или идеи формализуются очень плохо,
  то у нас есть две около-универсальных модели:
  полносвязная нейросеть и градиентный бустинг.
  И формальные методы, как их тестировать на адекватность
  - кроссвалидация.
  
  Да, есть специализированные нейросети - конволюционная, например,
  но я сейчас про полносвязную однонаправленную.
  
  И насколько я понимаю, параметры ищет градиентный спуск
  (+эволюция для гиперпараметров),
  а нейронка - это продукт его работы.
  
  В общем, я прошу пояснить,
  как тогда в вашем представлении выглядит универсальный ML,
  который строит модели данных без значимых априорных данных,
  и почему полносвязная нейросеть/бустинг деревьев/AIXI
  не подходят под это определение?
  
  napa3um
  4 авг 2019 в 10:02
  
  Универсальный ML пока что никак не выглядит,
  по всем тем причинам, которые вы и описываете.
  Универсального решения любой задачи не существует.
  И полносвязная нейронка тоже <увидит> лишь те решения,
  которые дизайнер фич позволит ей увидеть.
  Всё это, конечно, не мешает случайно натыкаться на решения чужими моделями,
  как и не мешает верить в чудо. :)
  
  Kilorad
  12 авг 2019 в 19:48
  
  Фичи повышают вероятность нахождения определённых решений.
  Но если запустить обучение достаточно надолго,
  и нейронка будет достаточно большая,
  то она сама построит эти фичи.
  На бесконечности построит, то есть недостаток предобработки
  можно компенсировать избытком вычислительных мощностей.
  Так что в пределе нейронка универсальна, разве нет?
  
  Есть другая модель, более теоретическая, но более обоснованно универсальная.
  AIXI. Если конкретнее, то AIXI Monte Carlo.
  Это поиск формул, которые могли бы породить данные,
  в алгоритмически полном пространстве.
  Например, у нас есть датасет Y=f(X),
  и AIXI перебирает множество программ на, скажем, Си,
  и все программы тестирует на то,
  насколько похожий датасет они порождают.
  Эта модель тоже неуниверсальна?
  
  Есть гибридные модели.
  Предобработчик на базе чего-то типа AIXI
  и постобработчик на базе нейросети/бустинга.
  Выполняет работу по дизайну фичей.
  Конечно, она может увидеть не все фичи,
  но человек же тоже не все полезные фичи видит.
  Такая модель неуниверсальна?
  
  А если в качестве предобработки используются верхние слои нейронки
  - то есть у нас сеть глубокая и полносвязная -
  это не сделает её универсальной в том же смысле,
  в котором универсальна человеческая способность искать шаблоны в данных?
  
  napa3um
  19 авг 2019 в 01:57
  
  Все эти ваши фантазии упираются только в одно -

.

  Иначе бы то, о чём вы говорите,
  действительно имело бы практическую ценность :).
  
  Kilorad
  23 авг 2019 в 17:49
  
  Вообще-то все эти модели работают.
  Ну как работают.
  Эволюционный поиск тоже работает.
  Глобальный оптимум не находит, но локальный - запросто.
  Вот и эти модели находят локально-оптимальные решения.
  Было бы странно от них ждать идеальных решений.
  
  Без модели исследуемого процесса не обойтись и в случае применения нейросетей,
  волшебным образом она модель не построит,
  она всего лишь инструмент для подбора свободных параметров в моделях,
  построенных людьми.
  
  Чем эта пачка из нейросетей, бустингов и AIXI
  так уж принципиально отличается от того, как модели строят люди?
  Понятно, что физическая база отличается
  и что элементарные блоки несколько иначе устроены.
  
  Но у меня сложилось впечатление, что вы полагаете,
  что люди могут строить модели каким-то таким образом,
  которым ML строить модели не может.
  Даже если данные и у людей, и у ML одни и те же.
  Даже если под ML подразумеваются любые ML
  из класса нейросетей, бустингов и AIXI,
  а так же любых их комбинаций.
  
  Я правильно понял вашу позицию?
  Если да, то можно поподробнее, так как мне это кажется странным?
  
  napa3um
  23 авг 2019 в 17:55
  
  Я говорю о том, что без проектирования модели задачи
  любой инструмент автоматического подбора параметров
  (будь то подкидывание монетки или нейросеть)
  будет бесполезен.
  Выводы о том, насколько схожи или не схожи нейросетки с биомозгами
  из этого тезиса сделать не получится.
  
  Kilorad
  23 авг 2019 в 18:15
  
  Когда человеческий мозг ищет некоторую закономерность в данных
  - это процесс автоматического подбора параметров?
  
  Он это делает с проектированием модели задачи или без?
  Если модель всё же используется,
  то её изготовление всегда происходит из какой-то другой модели
  или иногда тем самым автоматическим подбором параметров?
  
  napa3um
  23 авг 2019 в 19:03
  
  Ваши вопросы не являются контраргументами к моему тезису,
  вы и дальше можете жонглировать словами сколько угодно :).
  Уверен, что вы поняли мой исходный тезис,
  но пытаетесь <юридически> решить вопрос :).
  
  Kilorad
  23 авг 2019 в 23:19
  
  Ок, хорошо. По моим наблюдениям,
  feature engeneering и грамотный ручной выбор модели
  позволяют решить задачу сильно быстрее,
  чем если бы мы использовали тру-универсальные инструменты вроде AIXI
  или околоуниверсальные вроде ff-нейросетей.
  Тем не менее, <грубая сила> позволяет уделять меньше внимания
  построению моделей.
  Это и грубая сила более полного перебора
  (например, в широком пространстве нейронок,
  а не в узком пространстве линейных моделей),
  и грубая сила бОльшего количества статистики.
  
  Тем не менее иногда встаёт вопрос
  - а как в данной конкретной задаче сделать именно универсальную модель?
  Не то, чтобы мы не могли провести feature engeneering
  - скорее, мы себе не доверяем.
  И считаем свой FE слишком неполным.
  А иногда нам и правда нужна широкая универсальность,
  потому что чёрт его знает, с чем там наш сервис столкнётся в будущем,
  и хорошо бы, чтобы он мог сам как-то это прожевать, без доработок.
  
  Некоторые люди сразу говорят, что широкой универсальности добиться
  (даже задорого)
  нельзя.
  Они обосновывают это вполне правильной математикой
  - NP-полные задачи никто не отменял.
  Но эта логика неприменима к реальным мозгам
  - люди решают очень широкий класс задач,
  особенно если им дать компьютер.
  И возникает вопрос - а если человек может,
  то как это на компе реализовать?
  Если человек может делать FE,
  то может, и ML может?
  (ну пусть не человек, а коллектив людей. Но может же?)
  
  Тут некоторые люди начинают высказывать идеалистические концепции,
  мол, если человек что-то может,
  то не факт, что это принципиально возможно для компьютера.
  
  Другие начинают говорить, что:
  Честно говоря, я не уверен, что понимаю их верно.
  Это моя недоработка, и я сейчас именно её пытаюсь исправить,
  задавая вопросы вам =)
  Но концепция видится следующая:
  ML - это прикладное построение моделей.
  Человек может формализовать в виде датасета лишь малую часть своих знаний,
  ещё у него есть всякие неявные догадки,
  и он их формализует, выбирая класс модели
  (свёрточная ли нейронка или линейная регрессия)
  и FE.
  Нам не нужна механика,
  умеющая из нейронки прямого распространения выточить свёрточную,
  потому что это слишком дорого.
  Да, такая механика возможна.
  Да природа примерно так и поступила.
  Безо всяких априорных данных сделала модель из:
  Из молекул, которые были <под рукой>.
  Довольно тупыми алгоритмами эволюции.
  Но это чересчур дорого, потому непрактично и неинтересно.
  Не могли бы вы поправить меня в данной позиции, если я неправ?
  
  Есть и третья позиция.
  Если человек что-то может, значит, может и ML.
  А чтобы конструкция работала, она должна уметь делать Meta-Learning.
  То есть должна быть какая-то механика такая,
  что после решения одной задачи наша модель меняется,
  и вторую задачу решает уже быстрее,
  если вторая задача похожа на первую.
  Например: Можно взять штук 100 разных топологий нейронок,
  прогнать их на 10к задач и собрать статистику,
  какие топологии работают лучше всего.
  И на новой задаче сначала пробовать наилучшие топологии,
  и весь этот рейтинг постоянно обновлять,
  после каждой новой решённой задачи.
  Я опытным путём проверял Meta-Learning под задачи Feature Engeneering.
  Эксперимент показал,
  что поначалу модель Meta-Learning запарывает задачи,
  но после серии попыток выходит на уровень модели
  с хорошим ручным Feature Engeneering.
  Даже несмотря на то, что в этом автоматизированном FE
  был очень урезанный набор функций,
  и подобрать <истинную> закономерность там было тупо невозможно,
  только апроксимация.
  Всё равно Meta-Learning отработал, как надо.
  
  Практически же Meta-Learning сейчас используется в распознавании изображений
  - есть обученная нейросеть, которая распознаёт всякие разные картинки,
  многие пользователи берут её в качестве отправной точки для своей нейросети.
  Обучение в результате требует меньше времени и статистики
  
  napa3um
  24 авг 2019 в 09:42
  
  <Универсальная модель к задаче>
  - это не такая уж и универсальная модель,
  потому что её каждый раз строит человек к каждой задаче.
  Ок, к каждому классу задач.
  Ок, использует общие подходы построения моделей в схожих задачах.
  НО ДЕЛАЕТ ЭТО ЧЕЛОВЕК.
  Не потому, что он умнее транзисторного процессора,
  а потому, что именно у него, этого человека,
  заключённого в физическую реальность, и возникла ЗАДАЧА :).
  У транзистора личных задач нет.
  
  //// Конец цитирования.
  
  27.04.2024 20:55
  
  Специально привел "многометровый" листинг комментариев практически полностью,
  чтобы показать, что при чтении этого материала, с качественной визуализацией
  мифической ступеньки/"башни", реализуемую парой/ансаблем согласованных нейронов,
  читатель практически гарантированно не может заметить самого главного "слона"
  - то что реальное поведение нейронок при аппроксимации "произвольной функции",
  существенно отличается от описанного.
  Впрочем, как и "проблема промежутков", остающаяся без особого внимания.
  И все споры разворачиваются только о применимости нейронок
  в тех или иных сценариях.
  А не то как они действительно работают,
  обучаясь реазиовывать эту "магическую аппроксимацию".
  
  Чтобы не быть голословным, приведу пару иллюстраций как реально выглядит
  аппроксимация нейронкой предельно простых функций:
  
  kraidiky1. Источник: https://habr.com/ru/articles/219647/  Рис. 7127f2f705bb2bb1aa265ba8a2a0fdda.png
  
  Источник: https://habr.com/ru/articles/219647/ Рис. 7127f2f705bb2bb1aa265ba8a2a0fdda.png
  https://habrastorage.org/r/w1560/getpro/habr/post_images/712/7f2/f70/7127f2f705bb2bb1aa265ba8a2a0fdda.png
  
  И если для предыдущей иллюстрации можно еще сказать,
  что нейронку просто недообучили из-за ограниченного обучающего датасета.
  То вот для следующей иллюстрации "эта отмазка не работает":
  
  kraidiky2. Источник: https://habr.com/ru/articles/219647/ Рис. f15eb201219777217526254c5e9a1558.png
  
  Источник: https://habr.com/ru/articles/219647/ Рис. f15eb201219777217526254c5e9a1558.png
  https://habrastorage.org/r/w1560/getpro/habr/post_images/f15/eb2/012/f15eb201219777217526254c5e9a1558.png
  
  Как видите реальная аппроксимация нейронкой "произвольной функции"
  имеет какие-то общие свойства
  - характерный "фрактальный"/"пилообразный" рисунок -
  существенно отличающийся от ступенчатого/"башенного" типа,
  описанного в данном материале.
  Этот реальный характер аппроксимации нейронок
  тоже имеет в основе "нелинейную функцию активации",
  хотя и реализуется это немного по-другому.
  
  Но разбор этого лучше перенести в другой фрагмент
  - этот и так получился великоват,
  но честное слово, не знаю, где его можно было бы "урезать",
  без утери и общей картинки и деталей
  и, что не менее важно, типичного восприятия такого важного и нужного материала,
  если следовать полностью в русле авторской логики подачи материала.
  Основная претензия к этой логике в том,
  что ВОЗМОЖНОСТЬ использования "нелинейной функции активации"
  не увязана с ПРАКТИКОЙ обучения параметров нейронки.
  И в итоге главное в понимании работы нейросети уходит "на задний план",
  а на передний план снова вылезает "необъяснимая магия нейросетей".
  
  
  =========
  
  Тральщик Наставник. Модель Kandinsky 2.1 нарисовала:  1aebf897258045c5b9ec97627a35993c_00000
  
  Тральщик Наставник. Модель Kandinsky 3.0 нарисовала:  751029a1bece46f4b70b8a955584c33c_res_00000_image
  
  Тральщик Наставник. Модель Kandinsky 3.0 нарисовала:  7c3c1468865a4bee87e601a110307d82_res_00000_image
  
  Тральщик Наставник. Модель Kandinsky 3.1 нарисовала:  b7661267d91f468f87deba1f33727005_res_00000_image
  Flag Counter
  Free counters!
  
 Ваша оценка:

Связаться с программистом сайта.

Новые книги авторов СИ, вышедшие из печати:
О.Болдырева "Крадуш. Чужие души" М.Николаев "Вторжение на Землю"

Как попасть в этoт список
Сайт - "Художники" .. || .. Доска об'явлений "Книги"