Сделал, наконец, рабочий пример нейросети (НС). Сначала прочитал теорию – объяснение механизма back propagation из русской вики. Вроде все понятно. За вечер написал программу для анализа цифр 0..9 из базы MNIST. Программа запускается, веса обновляются, работа кипит. У любого программиста есть вера в чудо. Что программа, которая компилируется при первом запуске сразу сработает корректно. Да, но нет, девочки. На выходе – 10% распознанных цифр. Ерунда, короче. Как если бы цифра определялась случайным образом.
Стал разбираться. Выяснилось, что в русской вики алгоритм описан сложно, неточно, замылено и недостаточно понятно для реализации без PhD в математике:
1) При изменении весов ребер НС их нужно умножать на коэффициент обучения. Иначе, метод градиентного спуска может застрять в локальном минимуме, так и не попав в глобальный минимум. Но не сказано, как подбирать этот коэффициент.
2) Не сказано, что после подсчета дельт для изменения весов на каждом слое, обновлять веса ребер нужно один раз для всех слоев сразу, а не сразу после подсчета каждой дельты.
3) Для скрытых слоев в правиле подсчета дельт сказано, что нужно умножить на веса исходящих ребер на их дельты. Это не так. Не дельты, а 3 из 4-х множителей в дельте. Т.е. из дельт нужно исключить сигнал на предыдущем узле. Т.е. умножить на: (Tj-Oj)*Oj*(1-Oj), а не на (Tj-Oj)*Oj*(1-Oj)*On, где n=j-1
4) Ничего не сказано про архитектуру слоев и ребер. Должны ли они быть Full-connected [FC] (каждый узел соединен с каждым следующим) и как изменить алгоритм если у нас не FC-слои.
5) Пишут про функцию ошибки НС, но никак в финальных расчетах алгоритма ее не используют (хотя из нее через производные выводят формулы подсчета).
6) Полно туманных разъяснений вида "ну, тут очевидно – надо взять частную производную dE/dw". Ага, очевидно. Целыми днями только этим и занимаюсь.
7) Нет нормального примера простой НС с проставленными числами для каждого сигнала, веса, узла и расчета как это считается хотя бы на одной итерации.
8) Неясно, нужно ли нормализовать входные сигналы (из 0..255, например, делать числа в интервале 0..1).
9) Говорят, в начале нужно выставить маленькие значения весов ребер. Неясно, насколько малые. 0..1 или 0..0,001?
Правильно говорят, нужна своя, православная вики за 2 млрд руб!
Надо бы залить на вики свой алгоритм работы НС с примером подсчета. Не знаю, почему так ужасно объясняют на русской вики. Возможно это диверсия. Т.е. админы все написали понятно, но каждую ночь на страницу заходят враги отечества из-за границы (и не только) и запутывают описание алгоритма.
Есть такая теория заговора, что в школах и университетах СССР специально так ужасно преподают английский, чтобы народ не разбежался по заграницам сразу после выпуска. Федеральная программа по противодействию утечке мозгов. Или как-то так. Товарищ майор, если я случайно раскрыл гос. тайну, подайте знак.
В итоге, я сделал все ошибки из возможных, поэтому считался мусор. После гугления и изучения многих примеров НС, нашел, наконец, отличный пример простой реализации НС. С формулами и числовым расчетом. Все понятно, хоть и на английском.
Ссылка: A Step by Step Backpropagation Eхample by Matt Mazur
https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/
Сделал реализацию по статье и вуаля! Все работает! См. код в конце статьи и на скринах.
Заметил, что когда нужно разобраться в какой-либо айтишной теме, то на английском описание лучше и на треть короче, чем русский текст. На-русском, обычно, объясняют от общего к частному, т.е. методом дедукции, как Шерлок Холмс завещал. Сначала дают кучу формул, а потом цифры. Это очень неудобно, особенно для тех, у кого слабо развито абстрактное мышление. На английском наоборот, дают от частного к общему, т.е. индукцию. Сначала на пальцах считают цифры, а затем говорят какие формулы за этим стоят и почему формулы именно такие.
Наверно есть плюсы в обоих подходах. В дедуктивном подходе (условно, советском), если школьник не врубается в тему, он отваливается на уроке и на него не тратят время. Те же, кто прошел дальше, идут во всякие физматы, становятся перельманами, уезжают преподавать матан в американские университеты, получают нобелевку и отказываются от нее, сходят с ума и уезжают жить назад в Купчино и т.п. В индуктивном же подходе (американском) кривая обучения более пологая, больше людей можно научить. Опять же, это подходит для эмигрантов со слабым знанием языка. Сильно прокачаться в теории сложно, но быстро овладеть практическими знаниями вполне.
Есть такая байка про русского программиста в США. Он заметил, что данные записываются в одну таблицу, а потом прибавляются в нее же и нужно выдать результат. Он переписал функцию и просто выдал наружу return х*х; И все такие – вау! Гениальный русский придумал ФОРМУЛУ! Хотя смех смехом, но знакомого программиста с руками оторвали в какую-то контору в США, когда ему дали простую задачу найти элемент массива по условию, а он такой "ба, так это же задача на сортировку графа по теореме такой то!", применил алгоритм и вуаля. А не стал писать свой велосипед, что ОЧЕНЬ ценится в ИТ.
Ура! Заново понял, что такое производная и обратный процесс – интегрирование (восстановление функции по её производной). Геометрический смысл производной – тангенс угла наклона касательной к графику функции в заданной точке. Т.е. мера "крутизны" графика функции, насколько быстро она ускоряется. Так все просто! Все эти:
Для производной: (х^2)' = 2х, т.е. степень "х" становится множителем и степень уменьшается на 1.
Для интеграла: d(х^2)= (х^3)/3, т.е. интеграл f(х) – это найти такое значение функции, производная которой равна нашей (нашему родимому интегралу)
… а для сложных функций вообще чума: https://ru.wikipedia.org/wiki/Производная_функции#Таблица_производных
Эта как по телику когда говорят "В последний квартал уменьшились темпы снижения роста вывода капитала заграницу". Чтобы понять, что сказал диктор, надо всего-то, взять 5ую производную от денежного потока.
Надо копать дальше. Делать анализ 0..9 для MNIST. Для простого примера текущий код ОК, но вообще, выбрал плохую архитектуру структуры данных. Неудобно обновлять кучу массивов.