Мы используем файлы cookie на этом сайте, чтобы улучшить ваш пользовательский опыт.
Пожалуйста, ознакомьтесь с Политикой конфиденциальности для получения более подробной информации.

AI
Python
Нейросеть в 11 строчек на Python
09.09.2024

В данном курсе будет описан алгоритм обратного распространения ошибок на примере двух небольших нейронных сетей, реализованных на Python.

 

Каждная нейронная сеть, тренируемая через обратное распространение (backpropagation), пытается использовать входные данные для предсказания выходных.

 

Обратное распространение, в самом простом случае, рассчитывает статистическое соответствие между входными и выходными данными для создания модели.

 

Нейросеть в два слоя

 

import numpy as np

 

# sigmoid function

def nonlin(x,deriv=False):

    if(deriv==True):

        return x*(1-x)

    return 1/(1+np.exp(-x))

    

# input dataset

X = np.array([  [0,0,1],

                [0,1,1],

                [1,0,1],

                [1,1,1] ])

    

# output dataset            

y = np.array([[0,0,1,1]]).T

 

# seed random numbers to make calculation

# deterministic (just a good practice)

np.random.seed(1)

 

# initialize weights randomly with mean 0

syn0 = 2*np.random.random((3,1)) – 1

 

for iter in range(10000):

 

    # forward propagation

    l0 = X

    l1 = nonlin(np.dot(l0,syn0))

 

    # how much did we miss?

    l1_error = y – l1

 

    # multiply how much we missed by the 

    # slope of the sigmoid at the values in l1

    l1_delta = l1_error * nonlin(l1,True)

 

    # update weights

    syn0 += np.dot(l0.T,l1_delta)

 

print(“Output After Training:”)

print(l1)

 

Output After Training:

[[ 0.00966449]

 [ 0.00786506]

 [ 0.99358898]

 [ 0.99211957]]

 

Переменные и их описания.

 

X — матрица входного набора данных; строки – тренировочные примеры

y – матрица выходного набора данных; строки – тренировочные примеры

l0 – первый слой сети, определённый входными данными

l1 – второй слой сети или скрытый слой

syn0 – первый слой весов, Synapse 0, объединяет l0 с l1.

“*” — поэлементное умножение – два вектора одного размера умножают соответствующие значения, и на выходе получается вектор такого же размера

“-” – поэлементное вычитание векторов

x.dot(y) – если x и y – это вектора, то на выходе получится скалярное произведение. Если это матрицы, то получится перемножение матриц. Если матрица только одна из них – это перемножение вектора и матрицы.

 

Разбор кода построчно

 

import numpy as np

 

Данной строчкой импортируется numpy, библиотека линейной алгебры.

 

def nonlin(x,deriv=False)

 

Данная функция создаёт «сигмоиду». Она ставит в соответствие любое число значению от 0 до 1 и преобразовывает числа в вероятности, а также имеет несколько других полезных для тренировки нейросетей свойств.

Эта функция также умеет выдавать производную сигмоиды (deriv=True). Это одно из её полезных свойств. Если выход функции – это переменная out, тогда производная будет out * (1-out).

 

X = np.array([  [0,0,1],

                [0,1,1],

                [1,0,1],

                [1,1,1] ])

 

Инициализация массива входных данных в виде numpy-матрицы. Каждая строка – тренировочный пример. Столбцы – это входные узлы. В данном случае получается 3 входных узла в сети и 4 тренировочных примера.

 

y = np.array([[0,0,1,1]]).T

 

Инициализирует выходные данные. “.T” – функция транспонирования. После транспонирования у матрицы y есть 4 строки с одним столбцом. Как и в случае входных данных, каждая строка – это тренировочный пример, и каждый столбец (в данном случае один) – выходной узел. У сети получается 3 входа и 1 выход.

 

np.random.seed(1)

 

Благодаря этому случайное распределение будет каждый раз одним и тем же. Это позволяет проще отслеживать работу сети после внесения изменений в код.

 

syn0 = 2*np.random.random((3,1)) – 1

 

Матрица весов сети. syn0 означает «synapse zero». Так как в реализуемой нейросети всего два слоя, вход и выход, то необходима одна матрица весов, которая их свяжет. Вектор l0 имеет размер 3, а l1 – 1. Поскольку все узлы в l0 связываются со всеми узлами l1, то для этого необходима матрица размерности (3, 1). Такая матрица инициализируется случайным образом, и среднее значение равно нулю. За этим стоит достаточно сложная теория.

 

for iter in range(10000):

    l0 = X

 

После этих строчек начинается основной код тренировки сети. Цикл с кодом повторяется многократно и оптимизирует сеть для набора данных.

Первый слой, l0, – это просто данные. В X содержится 4 тренировочных примера. Нейронная сеть обрабатывает их все и сразу – это называется групповой тренировкой [full batch].

 

l1 = nonlin(np.dot(l0,syn0))

 

Это шаг предсказания. Нейронная есть пытается предсказывать вывод на основе ввода. Далее будет оцениваться качество предсказания для исправления в нужную сторону. В строке содержится два шага. Первый делает матричное перемножение l0 и syn0. Второй передаёт вывод через сигмоиду.

 

В приведенном примере было загружено 4 тренировочных примера, и получено 4 догадки (матрица 4х1). Каждый вывод соответствует догадке сети для данного ввода.

 

    l1_error = y – l1

 

Поскольку в l1 содержатся догадки, можно сравнить их разницу с реальностью, вычитая её l1 из правильного ответа y. l1_error – вектор из положительных и отрицательных чисел, характеризующий «промах» сети.

 

    l1_delta = l1_error * nonlin(l1,True)

 

Данная строка – это то, благодаря чему нейронная сеть может обучаться. Ее нужно разбирать по частям.

 

Первая часть: производная

 

nonlin(l1,True)

 

l1 представляет три этих точки, а код выдаёт наклон линий, показанных ниже. При больших значениях вроде x=2.0 (зелёная точка) и очень малые, вроде x=-1.0 (фиолетовая) линия имеют небольшой уклон. Самый большой угол у точки х=0 (голубая). Это имеет большое значение. Также заметно, что все производные лежат в пределах от 0 до 1.

Полное выражение: производная, взвешенная по ошибкам

 

Математически существуют более точные способы, но в данном случае подходит и этот. l1_error – это матрица (4,1). nonlin(l1,True) возвращает матрицу (4,1). Они поэлементно перемножаются и на выходе тоже получаем матрицу (4,1), l1_delta.

 

Умножая производные на ошибки, уменьшаются ошибки предсказаний, сделанных с высокой уверенностью. Если наклон линии был небольшим, то в сети содержится либо очень большое, либо очень малое значение. Если догадка в сети близка к нулю (х=0, у=0,5), то она не особенно уверенная. Неуверенные предсказания обновляются и сохраняются предсказания с высокой уверенностью, умножая их на величины, близкие к нулю.

 

    syn0 += np.dot(l0.T,l1_delta)

 

Далее сеть обновляет свои веса и переходит на следующую эпоху по циклу.

 

Нейросеть в три слоя

 

import numpy as np

 

def nonlin(x,deriv=False):

if(deriv==True):

    return x*(1-x)

return 1/(1+np.exp(-x))

    

X = np.array([[0,0,1],

            [0,1,1],

            [1,0,1],

            [1,1,1]])

                

y = np.array([[0],

[1],

[1],

[0]])

 

np.random.seed(1)

 

# randomly initialize our weights with mean 0

syn0 = 2*np.random.random((3,4)) – 1

syn1 = 2*np.random.random((4,1)) – 1

 

for j in range(60000):

 

# Feed forward through layers 0, 1, and 2

    l0 = X

    l1 = nonlin(np.dot(l0,syn0))

    l2 = nonlin(np.dot(l1,syn1))

 

    # how much did we miss the target value?

    l2_error = y – l2

    

    if (j% 10000) == 0:

        print(“Error:” + str(np.mean(np.abs(l2_error))))

        

    # in what direction is the target value?

    # were we really sure? if so, don’t change too much.

    l2_delta = l2_error*nonlin(l2,deriv=True)

 

    # how much did each l1 value contribute to the l2 error (according to the weights)?

    l1_error = l2_delta.dot(syn1.T)

    

    # in what direction is the target l1?

    # were we really sure? if so, don’t change too much.

    l1_delta = l1_error * nonlin(l1,deriv=True)

 

    syn1 += l1.T.dot(l2_delta)

    syn0 += l0.T.dot(l1_delta)

 

Error:0.496410031903

Error:0.00858452565325

Error:0.00578945986251

Error:0.00462917677677

Error:0.00395876528027

Error:0.00351012256786

 

Переменные и их описания

 

X – матрица входного набор данных; строки – тренировочные примеры

y – матрица выходного набора данных; строки – тренировочные примеры

l0 – первый слой сети, определённый входными данными

l1 – второй слой сети, или скрытый слой

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

syn0 – первый слой весов, Synapse 0, объединяет l0 с l1.

syn1 – второй слой весов, Synapse 1, объединяет l1 с l2.

l2_error – промах сети в количественном выражении

l2_delta – ошибка сети, в зависимости от уверенности предсказания. Почти совпадает с ошибкой, за исключением уверенных предсказаний

l1_error – взвешивая l2_delta весами из syn1, мы подсчитываем ошибку в среднем/скрытом слое

l1_delta – ошибки сети из l1, масштабируемые по уверенности предсказаний. Почти совпадает с l1_error, за исключением уверенных предсказаний

 

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

 

    l1_error = l2_delta.dot(syn1.T)

 

Нейросеть использует ошибки, взвешенные по уверенности предсказаний из l2, чтобы подсчитать ошибку для l1. В итоге получается ошибка, взвешенная по вкладам – подсчитывается, какой вклад в ошибки в l2 вносят значения в узлах l1. Этот шаг и называется обратным распространением ошибок. Затем обновляются веса syn0, используя тот же алгоритм, что и в варианте с нейросетью из двух слоёв.

Источник: http://iamtrask.github.io/2015/07/12/basic-python-network/ https://habr.com/ru/articles/271563/