Заранее хочу отметить, что тот кто знает как обучается персептрон — в этой статье вряд ли найдет что-то новое. Вы можете смело пропускать ее. Почему я решил это написать — я хотел бы написать цикл статей, связанных с нейронными сетями и применением TensorFlow.js, ввиду этого я не мог опустить общие теоретические выдержки. Поэтому прошу отнестись с большим терпением и пониманием к конечной задумке.

При классическом программировании разработчик описывает на конкретном языке программирования определённый жестко заданный набор правил, который был определен на основании его знаний в конкретной предметной области и который в первом приближении описывает процессы, происходящие в человеческом мозге при решении аналогичной задачи.

Например, может быть запрограммирована стратегия игры в крестики-нолики, шахмат и другое (рисунок 1).


Рисунок 1 – Классический подход решения задач

В то время как алгоритмы машинного обучения могут определять набор правил для решения задач без участия разработчика, а только на базе наличия тренировочного набора данных.
Тренировочный набор — это какой-то набор входных данных ассоциированный с набором ожидаемых результатов (ответами, выходными данными). На каждом шаге обучения, модель за счет изменения внутреннего состояния, будет оптимизировать и уменьшать ошибку между фактическим выходным результатом модели и ожидаемым результатом (рисунок 2).


Рисунок 2 – Машинное обучение

Нейронные сети


Долгое время учёные, вдохновляясь процессами происходящими в нашем мозге, пытались сделать реверс-инжиниринг центральной нервной системы и попробовать сымитировать работу человеческого мозга. Благодаря этому родилось целое направление в машинном обучении — нейронные сети.

На рисунке 3 вы можете увидеть сходство между устройством биологического нейрона и математическим представлением нейрона, используемого в машинном обучении.


Рисунок 3 – Математическое представление нейрона

В биологическом нейроне, нейрон получает электрические сигналы от дендритов, модулирующих электрические сигналы с разной силой, которые могут возбуждать нейрон при достижении некоторого порогового значения, что в свою очередь приведёт к передаче электрического сигнала другим нейронам через синапсы.

Персептрон


Математическая модель нейронной сети, состоящего из одного нейрона, который выполняет две последовательные операции (рисунок 4):

  • вычисляет сумму входных сигналов с учетом их весов (проводимости или сопротивления) связи

    ${sum=\ \vec{X}}^T\vec{W}+\vec{B}=\sum_{i=1}^{n}{x_iw_i}+b$

  • применяет активационную функцию к общей сумме воздействия входных сигналов.

    $out=\varphi(sum)$




Рисунок 4 – Математическая модель персептрона

В качестве активационной функции может использоваться любая дифференцируемая функция, наиболее часто используемые приведены в таблице 1. Выбор активационной функции ложиться на плечи инженера, и обычно этот выбор основан или на уже имеющемся опыте решения похожих задач, ну или просто методом подбора.

Заметка


Однако есть рекомендация – что если нужна нелинейность в нейронной сети, то в качестве активационной функции лучше всего подходит ReLU функция, которая имеет лучшие показатели сходимости модели во время процесса обучения.
Таблица 1 - Распространенные активационные функции
Имя Формула График
Linear function

$\varphi\left(x\right)=x$

.
Sigmoid function

$\varphi\left(x\right)=\frac{1}{1+e^{-x}}$


Softmax function

$\varphi\left(x_j\right)=\frac{e^{x_j}}{\sum_{i} e^{x_i}}$

$\varphi \left ( \begin{bmatrix} 1.2\\ 0.9\\ 0.4 \end{bmatrix} \right ) = \begin{bmatrix} 0.46\\ 0.34\\ 0.20 \end{bmatrix} $


Используется для задач классификации (где количество классов более 2)
Hyperbolic Tangent function

$\varphi\left(x\right)=\frac{e^x-e^{-x}}{e^x-e^{-x}}$



Сжимает входной сигнал в интервале [-1, 1]. Применяется в скрытых слоях нейронной сети, так как центрирует выходной сигнал относительно нуля, что ускоряет процесс обучения
Rectified Linear Unit (ReLU)

$\varphi\left(x\right)=\max(0,x)$



Используется в скрытых слоях нейронной сети, имеет лучшую сходимость, чем sigmoid и tanh функции
Leaky ReLU

$\varphi\left(x\right)=\max(0.01x,x)$



Лишен недостаток ReLU функции в интервале отрицательных выходных сигналов, где частичная производная равна 0


Процесс обучения персептрона


Процесс обучения состоит из несколько шагов. Для большей наглядности, рассмотрим некую вымышленную задачу, которую мы будем решать нейронной сетью, состоящей из одного нейрона с линейной активационной функции (это по сути персептрон без активационной функции вовсе), также для упрощения задачи – исключим в нейроне узел смещения b (рисунок 5).


Рисунок 5 – Обучающий набор данных и состояние нейронной сети на предыдущем шаге обучения

На данном этапе мы имеем нейронную сеть в некотором состоянии с определенными весами соединений, которые были вычислены на предыдущем этапе обучения модели или если это первая итерация обучения – то значения весов соединений выбраны в произвольном порядке.

Итак, представим, что мы имеем некоторый набор тренировочных данных, значения каждого элемента из набора представлены вектором входных данных (input data), содержащих 2 параметра (feature) $x_1,x_2$. Под $x_1,x_2$ в модели в зависимости от рассматриваемой предметной области может подразумеваться все что угодно: количество комнат в доме, расстояние дома от моря, ну или мы просто пытаемся обучить нейронную сеть логической операции И, или ИЛИ.

Каждый вектор входных данных в тренировочном наборе сопоставлен с вектором ожидаемого результата (expected output). В данном случае вектор выходных данных содержит только один параметр, которые опять же в зависимости от выбранной предметной области может означать все что угодно – цена дома, результат выполнения логической операции И или ИЛИ.

ШАГ 1 — Прямое распространение ошибки (feedforward process)
На данном шаге мы вычисляем сумму входных сигналов с учетом веса каждой связи и применяем активационную функцию (в нашем случае активационной функции нет). Сделаем вычисления для первого элемента в обучающем наборе:

$y_{predicted}=\sum_{i=1}^{n}{x_iw_i}=1\cdot0.1+0.5\cdot0.2=0.2$




Рисунок 6 – Прямое распространение ошибки

Обратите внимание, что написанная формула выше – это упрощенное математическое уравнение для частного случая операций над тензорами.

Тензор – это по сути контейнер данных, который может иметь N осей и произвольное число элементов вдоль каждой из осей. Большинство с тензорами знакомы с математики – векторы (тензор с одной осью), матрицы (тензор с двумя осями – строки, колонки).
Формулу можно написать в следующем виде, где вы увидите знакомые матрицы (тензоры) и их перемножение, а также поймете о каком упрощении шла речь выше:

${\vec{Y}}_{predicted}=\ {\vec{X}}^T\vec{W}=\left[\begin{matrix}x_1\\x_2\\\end{matrix}\right]^T\cdot \left [ \begin{matrix} w_1\\ w_2 \end{matrix} \right ]=\left [ \begin{matrix} x_1 & x_2 \end{matrix} \right ] \cdot \left [ \begin{matrix} w_1\\ w_2 \end{matrix} \right ] =\left [ x_1w_1+x_2w_2 \right ]$



ШАГ 2 — Расчет функции ошибки
Функция ошибка – это метрика, отражающая расхождение между ожидаемыми и полученными выходными данными. Обычно используют следующие функции ошибки:
среднеквадратичная ошибка (Mean Squared Error, MSE) – данная функция ошибки особенно чувствительна к выбросам в тренировочном наборе, так как используется квадрат от разности фактического и ожидаемого значений (выброс — значение, которое сильно удалено от других значений в наборе данных, которые могут иногда появляться в следствии ошибок данных, таких как смешивание данных с разными единицами измерения или плохие показания датчиков):

$L=\frac{1}{N}\sum_{i=1}^{N}\left(y_{predicted(i)}-y_{expected(i)}\right)^2$


среднеквадратичное отклонение (Root MSE) – по сути это тоже самое что, среднеквадратичная ошибка в контексте нейронных сетей, но может отражать реальную физическую единицу измерения, например, если в нейронной сети выходным параметров нейронной сети является цена дома выраженной в долларах, то единица измерения среднеквадратичной ошибки будет доллар квадратный ($$^2$), а для среднеквадратичного отклонения это доллар ($), что естественно немного упрощает задачу анализа человеком:

$L=\sqrt{\frac{1}{N}\sum_{i=1}^{N}\left(y_{predicted(i)}-y_{expected(i)}\right)^2}$


среднее отклонение (Mean Absolute Error, MAE) -в отличии от двух выше указанных значений, является не столь чувствительной к выбросам:

$L=\frac{1}{N}\sum_{i=1}^{N}\left|y_{predicted(i)}-y_{expected(i)}\right|$


перекрестная энтропия (Cross entropy) – использует для задач классификации:

$L=-\sum_{i=1}^{N}\sum_{j=1}^{M}{y_{expected(ij)}\log(y_{predicted(ij)})}$


где
$N$ – число экземпляров в тренировочном наборе
$M$ – число классов при решении задач классификации
$y_{expected}$ — ожидаемое выходное значение
$y_{predicted}$ – фактическое выходное значение обучаемой модели

Для нашего конкретного случая воспользуемся MSE:

$L=\frac{1}{N}\sum_{i=1}^{N}\left(y_{predicted(i)}-y_{expected(i)}\right)^2={(0.2-1)}^2=0.64$



ШАГ 3 — Обратное распространение ошибки (backpropagation)
Цель обучения нейронной сети проста – это минимизация функции ошибки:

$L\rightarrow min$


Одним способом найти минимум функции – это на каждом очередном шаге обучения модифицировать веса соединений в направлении противоположным вектору-градиенту – метод градиентного спуска, и это математически выглядит так:

${\vec{w}}^{(k+1)}={\vec{w}}^k-\mu\nabla L({\vec{w}}^k)$


где $k$ – k -ая итерация обучения нейронной сети;
$\mu$ – шаг обучения (learning rate) и задается инженером, обычно это может быть 0.1; 0.01 (о том как шаг обучения влияет на процесс сходимости обучения отметить чуть позже)
$\nabla L$ – градиент функции-ошибки
Для нахождения градиента, используем частные производные по настраиваемым аргументам $w_1,w_2$:

$\nabla L\left(\vec{w}\right)=\left[\begin{matrix}\frac{\partial L}{\partial w_1}\\\vdots\\\frac{\partial L}{\partial w_N}\\\end{matrix}\right]$


В нашем конкретном случае с учетом всех упрощений, функция ошибки принимает вид:

$L\left(w_1,w_2\right)={(y_{predicted}-y_{expected})}^2={(x_1w_1+x_2w_2-y_{expected})}^2=$

$={(1\cdot w_1+0.5\cdot w_2-1)}^2$


Памятка формул производных
Напомним некоторые формулы производных, которые пригодятся для вычисления частных производных

$\frac{d}{dx}c=0;c=const$

$\frac{d}{dx}\left[cf\left(x\right)\right]=cf^\prime\left(x\right);\ c=const$

$\frac{d}{dx}x^n=nx^{n-1}$

$\frac{d}{dx}\left[f\left(x\right)\pm g(x)\right]=f^\prime\left(x\right)\pm g^\prime(x)$

$\frac{d}{dx}\left[f\left(x\right)g\left(x\right)\right]=f^\prime\left(x\right)g\left(x\right)+g^\prime\left(x\right)f\left(x\right)$

$\frac{d}{dx}f\left(g\left(x\right)\right)=f^\prime(g(x))g^\prime(x)$



Найдем следующие частные производные:

$\frac{\partial}{\partial w_1}{(w_1+0.5w_2-1)}^2=2\cdot\left(w_1+0.5w_2-1\right)\frac{\partial}{\partial w_1}\left(w_1+0.5w_2-1\right)=$

$=2\cdot\left(w_1+0.5w_2-1\right)\cdot1=2\left(0.1+0.5\cdot0.2-1\right)=-1.6$


$\frac{\partial}{\partial w_2}{(w_1+0.5w_2-1)}^2=2\cdot\left(w_1+0.5w_2-1\right)\frac{\partial}{\partial w_2}\left(w_1+0.5w_2-1\right)=$

$=2\cdot\left(w_1+0.5w_2-1\right)\cdot0.5=2\left(0.1+0.5\cdot0.2-1\right)\cdot0.5=-0.8$



Тогда процесс обратного распространения ошибки – движение по модели от выхода по направлению к входу с модификацией весов модели в направлении обратном вектору градиента. Задавая обучающий шаг 0.1 (learning rate) имеем (рисунок 7):

$w_1^{(k+1)}=w_1^{(k)}-\mu\frac{\partial L\left(w_1,w_2\right)}{\partial w_1}=0.1-0.1\cdot\left(-1.6\right)=0.26$


$w_2^{(k+1)}=w_2^{(k)}-\mu\frac{\partial L\left(w_1,w_2\right)}{\partial w_2}=0.2-0.1\cdot\left(-0.8\right)=0.28$



Рисунок 7 – Обратное распространение ошибки
Таким образом мы завершили k+1 шаг обучения, чтобы убедиться, что ошибка снизилась, а выход от модели с новыми весами стал ближе к ожидаемому выполним процесс прямого распространения ошибки по модели с новыми весами (см. ШАГ 1):

$y_{predicted}=x_1w_1+x_2w_2=1\cdot0.26+0.5\cdot0.28=0.4$


Как видим, выходное значение увеличилось на 0.2 единица в верном направлении к ожидаемому результату – единице (1). Ошибка тогда составит:

$L={(0.4-1)}^2=0.36$


Как видим, на предыдущем шаге обучения ошибка составила 0.64, а с новыми весами – 0.36, следовательно мы настроили модель в верном направлении.

Следующая часть статьи:
Машинное обучение. Нейронные сети (часть 2): Моделирование OR; XOR с помощью TensorFlow.js