Содержание
Слои
Вернемся к пациентам в нашей условной больнице. Откроем ранее подготовленную книгу и переключимся сразу на третий лист 'sigma'. Это то место, где будут храниться те самые нейроны упрощенные до элементарных функций. В прошлый раз мы договорились о том, что в качестве входных данных мы будем использовать средние значения показателей температуры тела пациентов. Так и сделаем. В первой строке в столбцах от 'A' до 'J' вставим соответствующие значения входных данных по первому пациенту. Это можно сделать либо путем копирования и вставки значений, что бы те не менялись при каждом вводе новой формулы, либо можно вставить в 'A1' ссылку на ячейку '=input!C2' (или '=$input.С2' для некоторых программ) и потянуть вправо. В этом случае ячейки будут обновляться, но это для нас пока не принципиально. Либо можно отключить автоматическое вычисление в настройках программы, а затем если надо пересчитать, можно сделать это принудительно клавишей F9 (CTRL+SHIFT+F9). Хотя, например, в Google Spreadsheet этого, на сколько я понял, сделать нельзя, или не так просто.
Отсюда и далее таблицы использованные в примерах можно будет открыть по ссылкам на Google Workspace. Что бы показания были похожи на реальные, немного подправим формулу получения данных на листе 'input' на формулу '=СЛУЧМЕЖДУ(364;400)/10'. И если ранее вы отключили автоматическое вычисление, нажмем F9. Кстати, клавиша F9 часто отвечает за запуск исходного кода программ в большинстве сред разработки программ, так что это еще один такой программистский скил.
Первая строчка будет обозначать сенсорные нейроны, через которые нейронка будет получать данные из внешней среды. А строку целиком будем называть слоем (layer). Поскольку нас не интересует то как работают сенсорные нейроны, то и вычислять они ничего не будут. Следующие строки, начиная со второй будут отвечать уже за вставочные нейроны и будут содержать функцию активации, введенную в предыдущей части. Слои содержащие уже "думающие" элементы называют скрытыми или промежуточными (hidden).
Ранее вскользь упоминалось о количестве необходимых нам элементов НС, где прозвучало число тридцать, как бы взятое с потолка. На самом деле оно имеет под собой основу. Перед тем как разводить бурную деятельность необходимо внести некоторую ясность в то, откуда эта цифра и как ее можно посчитать заранее. Сколько нейронов должно быть в каждом слое, сколько этих слоев необходимо и достаточно. Что бы точно ответить на эти вопросы возможно понадобится и не один раздел.
Последовательное
Разберем пример, данный в качестве иллюстрации к процессу мышления, более подробно. Потому что тот, казалось бы, короткий ряд рассуждений изнутри гораздо сложнее. Что мы сделали для оценки среднего значения произвольного числового ряда для облегчения решения задачи? Мы для наглядности расставили числа в порядке возрастания. Возникает проблема: это ведь тоже некоторая операция, и вообще что такое наглядность? Наглядность она ведь для человека - существа, обладающего зрением, для машины никакой наглядности не существует. Она не знает о таких концепциях как порядок, возрастание, убывание. Более того, машина пока не понимает что такое больше и меньше. Если в человеке эта концепция заложена от рождения, и даже ребенок понимает, что нечто одно больше другого, то машине мы вынуждены пояснять это отдельно.
Разложим все по порядку. Сперва нужно пояснить машине, что числа бывают одно больше другого и наоборот. Если рассматривать такую задачу в рамках НС, то это будет отдельная нейронка, которую надо сперва обучить на входе из двух чисел и натренировать ее на определение знака неравенства. Если же их сразу двенадцать, то очевидно, что для обучения упорядочению случайным образом поступающих рядов нейронке придется затратить значительно больше ресурсов и времени. Попробуйте первоклашке объяснить, что вы хотите получить от него в результате, не используя слов, даже учитывая то, что ребенок уже понимает, что одно яблоко больше второго. Когда-нибудь, наверное, ребенок догадается и выработает концепцию, но это будет весьма длительное и довольно бессмысленное занятие. То есть понадобится неадекватное задаче решение и непонятное количество ресурсов - в нашем случае нейронов.
Поэтому немного смошенничаем и не будем грузить нашу нейронку лишними измышлениями и дадим ей ряд уже отсортированных значений. Таким образом, мы разложили задачу на некую последовательность, которая упростит и нам и машине жизнь. Важно осознать, что это именно последовательность действий: нельзя сперва определить порядок, а потом ввести смысл неравенства. К счастью, экселька без каких-то глубоких измышлений умеет делать и то и другое, а именно организовать массивы в заданном порядке. Выделим диапазон 'A1:J1', найдем пункт меню работы с данными и выберем сортировку.
В случае с Google Spreadsheet придется немного поколдовать дополнительно, так как последний, оказывается, не умеет сортировать по горизонтали '=ТРАНСП(SORT(ТРАНСП(input!C2:L2);1; ИСТИНА()))'. Обратите внимание, что мы уже ходим возле крайней границы встроенных возможностей. Но это так, отступление.
В таком виде нейронке будет значительно легче понять что мы от нее хотим. Более того, в примере с рядом случайных чисел из предыдущей части был сделан еще один финт ушами. Была поставлена задача не вычислить среднее, а выбрать какое из чисел ближе всего к среднему, что означает несколько, а может и вовсе, другое. К примеру, какое из чисел 1, 2, 9, 10 ближе к среднему? Во-первых это сразу два числа: 2 и 9; во-вторых, ни то не другое в общем не намного ближе к среднему чем 1 или 10. С другой стороны, мы не предполагаем угадывание какого-то пятого заранее неизвестного числа. Может это 5, а может 6? Или нужен точный ответ 5,75? То есть было бы разумнее переформулировать задачу таким же образом, что бы не заставлять НС гадать. Забегая вперед, надо сказать, что гадать она все-равно будет, но ей в таком случае надо будет лишь определиться с выбором существующего числа и его позиции, а не выдумывать что-то новое.
Параллельное
Что бы на практике посмотреть что же такое функция активации представим опять живую клетку. В мозг поступают одновременно произвольное количество сигналов от органов чувств разной интенсивности и длительности, в нервной системе нет понятия такта с которым работает машина. За один такт процессор в вашем компьютере может обработать лишь некоторое ограниченное количество команд. Этот параметр напрямую связан с количеством так называемых ядер. Вообще говоря, одно ядро выполняет строго одну операцию за такт. Есть там конечно некоторые ухищрения за счет "холостых" тактов, которые позволяют говорить о, как-будто бы, параллельном исполнении, но это инженерные тонкости.
А вот нервным клеткам совершенно по барабану чем занимаются в данный момент времени большинство других клеток мозга. Им есть дело лишь до тех, с которыми у нее налажены связи. Соседние клетки могут посылать сигналы по отдельности, все вместе, с разными интервалами времени и разной интенсивностью. Мозг постоянно обрабатывает миллионы импульсов и отправляет назад чуть меньше, но тоже довольно внушительное количество, управляющих команд мышцам, что бы как минимум дышать и не падать, железам, что бы варить пищу, бояться или радоваться и много всего прочего каждую секунду. Это ему удается как-раз за счет того, что одновременно могут быть активны не одна или восемь ядер, а те же миллионы клеток.
Зато машина, хоть и последовательно, но исполняет очень много операции за то же время. Поэтому удобно ввести еще одно условное понятие входного вектора или набора данных (input dataset). Один входной вектор будет поступать в нашу нейронку за один раз и будет иметь один определенный результат. Если результат заранее известен, то это обучающий набор данных (training dataset). Этим самым мы сами оговариваем, что в отличии от живых нейронов элементы НС получают дискретные последовательные сигналы по одному за раз, но работать они будут с каждым входным вектором как-будто бы параллельно.
Вот теперь со спокойной душой можно ввести заветную функцию активации в наш будущий нейрон. Переключимся на лист 'sigma' и в ячейку A2 введем '=ЕСЛИ(1/(1+EXP(-(СУММ(A1:J1)-38,2*12)))>0,5;1;0)'.
C вероятностью 1/2 вам выведется 0 или 1. Вот, собственно, и действующий нейрон. Он умеет принимать на вход энное количество сигналов и выдает в ответ всего лишь 1 или 0 - отправить сигнал дальше соседям или же промолчать. Выглядит, может быть не очень очевидно, поэтому можно немного разобрать что же происходит. А буквально нейрон собрал сумму сигналов сенсорного слоя 'СУММ(A1:J1)', сместил её в начало оси координат '38,2*12' для удобства (bias), и применил к результату сигмоид (или логистическую функцию). И если результатом функции получилось значение больше 0,5 вывел 1 (активировался), в противном случае проигнорировал. Зачем нужен сигмоид и чем его можно еще заменить, поговорим позже, а пока этого достаточно.
Теперь попробуем клонировать его в какие-нибудь другие ячейки. Если воспользоваться автозаполнением ячеек и потянуть наш нейрон вправо, то кое что пойдет не так: в следующую ячейку встанет не совсем нужное значение '=ЕСЛИ(1/(1+EXP(-(СУММ(B1:K1)-38,2*10)))>0,5;1;0)'. Тут экселька нас не поняла и решила, что мы хотим получить другой нейрон, реагирующий на несколько другие сенсоры. Что бы объяснить ей что ячейки, как и нейроны в реальности должны быть совершенно одинаковыми зафиксируем столбцы специальным символом '$'. В результате в ячейке 'A1' должно получиться '=ЕСЛИ(1/(1+EXP(-(СУММ($A1:$J1)-38,2*10)))>0,5;1;0)'. Вот теперь экселька понимает, что столбцы трогать не надо и заполнит все так как следует. Для красоты и ровного счета сделаем десять нейронов в столбцах от 'B' до 'J'. Ну, не совсем, конечно, количество связано с эстетикой, но вернемся к этому потом, если выбор окажется по каким-то причинам неверным.
Итого имеем десять абсолютно одинаковых элементов связанных неким образом со всеми десятью сенсорами. Но, очевидно, что чего-то не хватает. Если дать на вход какие-то другие данные, то очень скоро можно убедиться, что состояния нейронов иногда меняются, но все одновременно и на одинаковые значения. Что впрочем не удивительно, учитывая их абсолютную одинаковость. Так что же отличает или должно отличать один нейрон от другого, что бы получалось что-то? если не осмысленное, то хотя бы разное. Несложно догадаться, что это что-то, наверное, должно быть в пропущенном ранее листе 'weight'.
Веса, вектора, матрицы
Живые клетки имеют одно свойство, которое мы до сих пор не рассматривали. Нейроны связаны между собой отростками дендритами - входами, и аксонами - выходами. Нейрон одним из своих дендритов подключается к аксону другого нейрона синаптической связью. Биологически это весьма сложная структура, которая химическим путем получает электрический потенциал от другой клетки. Эта сложная химия зависит как от физических свойств - надежности контакта, так и от наличия или отсутствия в окружающей среде разных химических соединений и молекул - медиаторов. Качество связи на каждом контакте разное и клетка может получать от своего абонента какое-то ограниченное количество этого потенциала. Которое, как мы уже привыкли делать, можно сопоставить с какими-то числами. Сразу напрашивается понятие процента. Мы можем условно обозначить качество связи от 0 до 1 в зависимости того какой процент заряда примет дендрит получатель от передающего нейрона.
Назовем такое соответствие весом (weight) связи и заполним этими весами второй лист. Поскольку мы пока не знаем какого качества связи нам нужны, то возьмем, снова, случайные числа 0 до 1. Можно взять и какие-то определенные числа, но это пока совершенно не имеет значения. Пусть в столбце 'weight!A' в строках с 1 по 10 содержатся связи нейрона 'sigma!A1' с сенсорным слоем '=СЛУЧМЕЖДУ(0;100)/100'. Распространим формулу на все нейроны и получим матрицу весов. На этот раз надо зафиксировать полученные значения, поэтому нужно вырезать и вставить их как конкретные значения, а не как формулу - мы не хотим, что бы они менялись произвольно.
Теперь, по логике каждый нейрон должен получать какую-то свою порцию заряда от каждого сенсора. Заменим сумму сигналов на сумму произведений сигналов на веса соответствующих связей 'СУММПРОИЗВ(A1:J1;ТРАНСП(weight!A1:A10))' - такая операция называется взвешиванием входного вектора. И подправим смещение в соответствии со средним весом всех связей. Для чего для каждого нейрона рассчитаем в строке 12 средний вес '=СРЗНАЧ(A1:A10)', и включим его в формулу функции активации 'sigma.A1' '=ЕСЛИ(1/(1+EXP(-
(СУММПРОИЗВ($A1:$J1;ТРАНСП(weight!A1:A10))-38,2*10*weight!A12)))>0,5;1;0)' и заполним остальные 9 ячеек. В результате каждый нейрон индивидуально реагирует на входной вектор, и если попробовать подать на ввод другие данные, то мы должны увидеть относительно случайный ряд нулей и единиц.
Такая простейшая компьютерная структура, выдающая некий результат согласно входным данным и весам связей нейронов называется в терминах НС перцептроном. Набор нулей и единиц плохо соответствует результату который бы мы хотели видеть. Но теоретически даже на данном этапе можно начинать подбирать веса связей таким образом, что бы исходящий набор данных имел какой-то смысл. Например, можно интерпретировать его как близость соответствующего входа среднему значению - примерно как в детской игре "тепло-холодно". И если входящий ряд имеет какие-то общие закономерности, например, что температура посередине отсортированного ряда почти всегда будет соответствовать средней, то веса теоретически можно настроить таким образом, что на выходе где-то посередине всегда будут единицы, а по краям нули.
Однако, этого как мы понимаем мало. Это больше выглядит как подгонка, чем реальный процесс обучения. Как только на вход такой сети подадут сигнал не подчиненный такой закономерности, перцептрон прогнозируемо выдаст неадекватный результат. Да и зачем нам результат в виде нескольких "теплых" вместо одного "горячего". А о том как получить более осмысленный результат предлагаю поговорить в следующий раз. Так же мы наконец попробуем реально потренировать нашу нейронку и, возможно, поймем за одно, что одними формулами в ячейках так запросто не обойтись и придется уже набрать клавиатуре что-то кроме цифр.
Продолжение следует...
PS. Материалы для более глубокого погружения
https://libraryno.ru/osnovnye-terminy-dlya-neyronnyh-setey-iis/