Искусственные нейронные сети (ИНС) или просто нейронные сети, по сути, представляют собой вычислительные системы, состоящие из сильно взаимосвязанных компонентов, которые фиксируют и представляют сложные отношения ввода-вывода. По сути, ИНС - это грубая попытка имитировать функциональность биологических нейронных сетей.

Обычно нейронные сети состоят из нескольких уровней «нейронов» или узлов, а также существует множество вариаций и комбинаций ИНС, таких как сверточные нейронные сети (CNN) и рекуррентные нейронные сети (RNN); В нашем случае мы собираемся создать простейший возможный пример, однослойную нейронную сеть с одним нейроном.

Простая нейронная сеть

Начнем с определения проблемы, которую наша сеть будет обучена решать:

Мы пытаемся найти значение из последнего примера, знайте для всех, кто уделяет достаточно внимания, выходное значение будет соответствовать значению входного крайнего левого столбца; Давайте посмотрим, сможем ли мы заставить нашу нейронную сеть правильно ответить на вопрос.

Основываясь на примерах входов и выходов, наш нейрон должен будет принимать 3 входа и возвращать один выход.

Как работает обучение?

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

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

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

Ясно, верно? Да, не беспокойтесь, я тоже запутался в этом вопросе; Давайте немного углубимся в другую концепцию нашей нейронной сети, чтобы лучше понять, что происходит во время обучения.

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

В нашем случае мы будем тренироваться с использованием известного набора данных, поэтому процесс будет примерно выглядеть следующим образом:

  • Загрузите обучающую выборку из известных примеров.
  • Установите случайные веса для каждого входа.
  • Передайте входные данные и вес в математическую формулу для расчета выхода.
  • Вычислите ошибку, то есть разницу между выходным сигналом нейрона и известным значением.
  • В зависимости от ошибки слегка отрегулируйте веса.
  • Промыть и повторить 10 000 раз.

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

Математика

Теперь вам может быть интересно, как рассчитываются фактические выходные значения; в этом случае мы используем сигмовидную функцию для вычисления выходных значений. Давайте быстро взглянем на математику.

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

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

Построенный результат вышеупомянутой функции будет выглядеть как следующая кривая:

Имейте в виду, что это не единственный тип нейрона, и в зависимости от типа нейронной сети и задания, которое мы ему даем, мы можем использовать другие виды функций для вычисления выходных данных.

Регулировка веса

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

В этом случае мы воспользуемся другой формулой машинного обучения «Производная, взвешенная с ошибкой» или правилом дельты.

Или лучше представить:

Эта формула скорректирует вес пропорционально выходному сигналу нейрона; Используя исходную сигмовидную кривую как часть расчета, мы убеждаемся, что чем увереннее нейрон, тем меньше будет корректировка.

Покажи мне код

Хорошо, хватит математики и теории, давайте продолжим и напишем немного Python. В первую очередь нам нужно будет включить следующие методы из библиотеки numpy:

  • массив: используется для создания матриц
  • random: генерирует случайное число
  • exp: вычисляет натуральную экспоненту
  • точка: используется для умножения матриц
from numpy import exp, array, random, dot

Затем нам нужно будет определить входы и выходы обучающего набора, для этого мы можем разделить их на 2 отдельные матрицы:

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T

После этого нам понадобится переменная для хранения неизвестного ввода:

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T 
#unknown input 
unk_input = array([1, 0, 0])

Также нам нужно сгенерировать набор весов для каждого из наших входов:

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T 
#unknown input 
un_input = array([1, 0, 0]) 
# initialize synapse_weights
random.seed(1)
sy_weights = 2 * random.random((3,1)) — 1

Приведенный выше код устанавливает начальное число для генератора случайных чисел, при этом будут возвращаться одни и те же случайные числа между запусками. Давайте напечатаем synapse_weights, чтобы увидеть результаты:

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T 
#unknown input 
un_input = array([1, 0, 0]) 
# initialize synapse_weights
random.seed(1)
sy_weights = 2 * random.random((3,1)) — 1 
print sy_weights

Что должно сгенерировать что-то вроде следующего:

[[-0.16595599]
 [ 0.44064899]
 [-0.99977125]]

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

Итак, что произойдет, если мы сможем запустить нашу нейронную сеть без обучения? Как вы думаете, что произойдет? Будет ли получен точный результат?

Давайте разберемся:

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T 
#unknown input
un_input = array([1, 0, 0]) 
# initialize synapse_weights
random.seed(1)
sy_weights = 2 * random.random((3,1)) — 1 
# output without untrained neuron
print 1 / (1 + exp(-(dot(un_input, sy_weights))))

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

Продолжайте и запустите приведенный выше код, вы можете ожидать увидеть что-то вроде следующего:

[ 0.45860596]

И, как вы можете видеть, это значение не очень точное, я думаю, мы вроде как, могли бы, возможно, сказать, что значение близко к 0, но это не сработает, мы ищем значение, максимально близкое к 1 или 0 .

С учетом сказанного, единственный оставшийся шаг - обучить нашу ИНС перед фактическим вычислением выходных данных, и мы сделаем это, запустив сигмовидную функцию 10 000 раз и настроив веса на каждой итерации.

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T 
#unknown input
un_input = array([1, 0, 0]) 
# initialize synapse_weights
random.seed(1)
sy_weights = 2 * random.random((3,1)) — 1 
# Weights before training
print sy_weights 
# train the network
for i in xrange(10000): 
     output = 1 / (1 + exp(-(dot(ts_inputs, sy_weights)))) 
     sy_weights += dot(ts_inputs.T, (ts_outputs — output) * output * (1 — output)) 
# Weights after training 
print sy_weights

Приведенный выше код позволит нам получить максимальную оценку обученных весов синапсов, которые в моем случае выглядят следующим образом:

# Before training
[[-0.16595599]
 [ 0.44064899]
 [-0.99977125]]

# After training
[[ 9.67299303]
 [-0.2078435 ]
 [-4.62963669]]

Есть разница, но приведет ли это к улучшению? Давайте узнаем, запустив следующий и последний фрагмент кода:

from numpy import exp, array, random, dot
ts_inputs = array([[0, 0, 0], [0, 0, 1], [0, 1, 1], [1, 0, 1], [1, 1, 1]])
ts_outputs = array([[0, 0, 0, 1, 1]]).T 
#unknown input
un_input = array([1, 0, 0]) 
# initialize synapse_weights
random.seed(1)
sy_weights = 2 * random.random((3,1)) — 1 
# train the network
for i in xrange(10000): 
    output = 1 / (1 + exp(-(dot(ts_inputs, sy_weights)))) 
    sy_weights += dot(ts_inputs.T, (ts_outputs — output) * output * (1 — output)) 
# Print the result for our unknown input 
print 1 / (1 + exp(-(dot(un_input, sy_weights))))

В результате чего;

[ 0.99993704]

Бац! Я бы сказал, что это довольно точный результат. Поэкспериментируйте, изменяя некоторые значения или пытаясь вычислить другой пример.

Заключение

Вы дошли до конца поста !! Поздравляю! Этот был длинным и отнюдь не всегда легким в освоении. Однако имейте в виду, что мы только царапаем поверхность, я бегло рассмотрел множество концепций и теорий, одна только математика может занять несколько постов, чтобы начать с нуля.

использованная литература

Эта статья изначально была размещена на моем собственном сайте.