Всем привет.   Я ML энтузиаст, и пытаюсь разобраться в нейронных сетях. Пока разбирался у меня появились вопросы, а возможно и ответы. Собственно, сразу один из моих вопросов: почему перцептрон с несколькими выходами не используется по умолчанию? Количество параметров полносвязного нейронного слоя можно сократить почти** не потеряв в точности! Для этого вместо привычного нейрона нужно использовать нейрон с несколькими выходами(будем называть его "новый").  Был нейрон:  Стал нейрон:  Новый нейрон состоит из вектора W(Нормали к поверхности), нескольких пар значений: [смещения гиперплоскости b, масштаба расстояния s] и функции активации. Самым "тяжеловесным" в обычном перцептроне является вектор нормали W. Соответственно, в "новом" нейроне предпринимается попытка экономии кол-ва параметров как раз за счет переиспользования нормали W несколько раз. Пусть n_surface_normal_reuse будет обозначать количество переиспользований нормалей, он же количество выходов у перцептрона. Теперь считаем сколько параметров имеет слой из классических нейронов и слой из "новых" перцептронов при равном количестве входов и выходов. #слой из обычных перцептронаi = 1000    # количество входов каждого слоя o = 1000   # количество выходов каждого слоя n = o         # количество нейронов каждого слоя w = i * n    # количество параметров матрицы W для слоя b = o         # количество параметров смещений  Bias print(w + b)  # количество параметров слоя  1001000 ~ миллион параметров  #слой из "новых" перцептроновi = 1000   # количество входов слоя o = 1000  # количество выходов слоя n_snr = n_surface_normal_reuse = 10    # количество выходов одного нейрона b = o       #  количество параметров смещений гиперплоскости bias s = o       #  количество параметров растяжений растояния до гиперплоскости scale n_w = o/n_snr      # количество нормалей к поверхности w = n_w * i           # количество параметров слоя матрицы W print(w + b + s)   # количество параметров слоя  102000.0 ~ сто тысяч параметров  Количество параметров в данном случае сократилось примерно в n_surface_normal_reuse раз. Смотрим на текущий общепринятый простой перцептрон: y = activate_f(ΣWi*Xi + b)  Чтобы лучше понимать что скрывается за этой формулой я я прибегну к различным интерпретациям.  Итак:  ΣWi*Xi + b = 0, есть ничто иное как гиперплоскость.  Если b = 0, то плоскость проходит через центр координат, а нормалью к этой гиперплоскости является вектор W.  Другими словами, вектор нормали W определяет углы(или повороты) гиперплоскости относительно системы координат.  Параметр b - влияет на параллельное смещение гиперплоскости относительно начала координат.  Cмотрим на расстояние от гиперплоскости до точки X (её координаты - это входной вектор нейрона [X1, X2,... X3]) . Формула расстояния выглядит как  (ΣWi*Xi + b) / ((ΣWi2)0.5)    Видно что в числителе наша формула обычного нейрона: ΣWi*Xi + b  Введем новый параметр scale = 1 / (ΣWi2)0.5, и перепишем формулу обычного перцептрона : \y = activate_f((ΣWi*Xi + b) * s), где W - нормаль,  b-смещение(bias), s - масштаб(scale).  Смотрим на перцептрон новыми глазами, видим что в нем:  a) считается расстояние от входной точки до гиперплоскости заданной вектором W и смещением b, б) полученное расстояние масштабируется( растягивается или сжимается), за это отвечает параметр s, в) Функция активации в свою очередь - просто принимает на вход "расяннутое" или "сжатое" расстояние от входной точки до гиперплоскости и "искажет" его. Расстояние, пропущенное через функцию активации, позволяет перевести число, которым выражено расстояние, в термины: далеко/близко, чем дальше тем лучше, берем по одну сторону/по другую не берем, и т. д.  Набор терминов зависит от выбранной функции активации, например:   если функция активации Relu, то ее можно перевести в следующие теримины: берем  точки ТОЛЬКО С ПОЛОЖИТЕЛЬНОЙ СТОРОНЫ(куда смотрит вектор Нормали W), и чем дальше точка  - тем лучше  Sigmoid - не берем точки, которые ДАЛЕКО ПО ОТРИЦАТЕЛЬНУЮ сторону, точно берем точки что ДАЛЕКО ПО ПОЛОЖИТЕЛЬНУЮ сторону, точки которые находятся "недалеко" от гиперплоскости берем с "заниженными" весами. Размер "НЕДАЛЕКО" регулируется как раз параметром s Sig - берем точки ТОЛЬКО С ПОЛОЖИТЕЛЬНОЙ стороны, причем у всех таких точек будут одинаковые веса. Точки с отрицательной стороны не берем TanH - очень похожа на Sigmoid, только точки которые далеко по отрицательную сторону гиперплоскости имеют вес ~-1.  Для Relu важен только знак параметра s. То есть для подобных функций нужно просто взять для половины выходов коэффициент равный s = +1 для другой  s = -1. Тренировать этот коэффициент не нужно.  Дальше вспоминаем что перцептрон так же реализует логику OR/AND/AND_NOT.То есть, если на первый слой смотреть в терминах Далеко/Близко от гиперплоскости или Берем по эту сторону/По другую не берем, а на следующий слой смотреть в терминах   OR/AND/AND_NOT то мы примерно можем прочитать что делает нейросеть или наоборот ее построить. Пусть у нас есть черный ящик. У черного ящика есть 2 входа и один выход. На выходе появляется всего 2 значения: 0 или 1. Предположим мы знаем логику черного ящика, но все равно попытаемся ее «аппроксимировать» с помощью нейросети. Истинная логика черного ящика простая - кольцо. Если входная точка попадает в кольцо - на выходе 1 (красный цвет) иначе 0 (белый); Если строить минимальную рабочую нейросеть на обычных перцептронах - то нам достаточно будет 2х скрытых слоев, в первом будет 6 нейронов, во втором 2, и 1 нейрон в выходном слое:  Интерпретация логики нейронной сети из обычных перцептронов получатся следующая: Первый слой - каждый нейрон функция расстояния от линии (зеленого или оранжевого цвета), всего 6 таких.  Во втором слое первый нейрон (зеленый) реализует логику что точка находиться внутри зеленого треугольника.  То есть растояние от первой зеленой гипеплоскости положительно "И" от второй зеленой гиперплоскости положительно "И" от третей гиперплоскости положительно.  Второй нейрон второго слоя(оранжевый) - та же логика "И", но с оранжевыми треугольником.  Выходной нейрон реализует следующую логику: (точка внутри зеленого треугольника) and not (точка внутри оранжевого треугольника)  Небольшое доказательство/рассуждение что на одном нейроне можно построить логику A and NOT(B): Если у нас функция активации предыдущего слоя Sigmoid - то выходные значения нейроном между 0 и 1. Отмечаем на плоскости красным точки когда выход должен быть равен нулю (красная точка) а когда единице (зеленая):   Видно что можно одной линией разделить пространство на две области, это может сделать один нейрон. Но так как из-за выбранной функции активации входное значение будет всегда меньше 1, а область которую нужно отделить является квадратом. Соответсвенно чтобы уменьшить погрешность лучше взять 2 нейрона вместо одного и добавить еще один скрытый слой. Теперь, предположим, нам нужно увеличить точность получившейся нейронной сети. Из рис 4 видно что точность достаточно сильно влияет 1й слой. Нам нужно больше нейронов, чтобы сделать "КРУГ" из гиперплоскостей более плавным.  Пусть теперь в первом скрытом слое будет 12 нейронов, во всех остальных слоях без изменений.  Теперь 6 нейронов используется для построения зеленой области, и 6 нейронов для построения оранжевой области:  Если внимательно присмотреться что происходит на последнем рисунке - видно что много линий ПОЧТИ параллельны друг другу.  Так же видно, что не важно под каким углом к системе координат расположены линии(в общем случае гиперплоскости). Их все можно повернуть на одинаковый градус, и точность от этого не измениться, потому что все что делают эти линии(гиперплоскости) «огораживают» нужную замкнутую область. И это как раз это позволяем нам тут воспользоваться "новым" нейроном с несколькими выходами. На рисунке ниже нарисованы гиперплоскости одного "нового" нейрона, с разными параметрами B и S :  То есть один "Новый" нейрон с 4мя выходами потенциально может заменить 4 обычных нейрона в первом скрытом слое. Новый нейрон может участвовать в "огораживании" сразу нескольких замкнутых областей. Понятно что в зависимости от задачи эффективность нейронов с несколькими выходами будет разной, но если количество входов и выходов слоя достаточно большое - то однозначно нужно пробовать. Начинать стоит c n_surface_normal_reuse = 4. Есть мысли что на вектор нормали W лучше смотреть как на вектор длинной = 1 и инициализировать Wi равномерным распределением R(x | -1, 1) поделенным на (ΣWi2)0.5. Для смещения b - равномерным распределением b ∈ R(x | -(ΣMAX(|Xi|)2)0.5, (Σ(MAX(|Xi|))2)0.5), где max(|Xi|) - максимальное значение по модулю iго входа перцептрона.   В частном случае, если максимальные значения входов |Xi|=1 - то b ∈ R(0, N0.5). Инициализация параметра s тоже похоже как-то зависит от Σ(MAX(|Xi|) и функции активации. Для Relu, как уже было сказано, это -1 или +1. Для семейства сигмойдных функций активаций - вопрос открытый. Данная статья не является научной работой, и по сути является вопросом. Возможно где-то допущены ошибки и ложные утверждения/предположения, я надеюсь, знающие люди поправят.