Построение целой нейронной сети прямого распространения

От логистической регрессии к нейронным сетям

Здравствуйте и вновь добро пожаловать на занятия по теме «Обработка данных: глубокое обучение на языке Python, часть 1».

В этой лекции мы плавно перейдём от логистической регрессии к нейронным сетям. Не забывайте, что логистическая регрессия – это и есть нейрон, а мы объединяем их вместе для создания сети нейронов. Самый простой способ для этого – метод прямого распространения, который вы изучите на этих занятиях.

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

a = x_1 w_1 + x_2 w_2 + b,

после чего всё это подставляем в сигмоиду:

p (y|x) = \frac{1}{1 + e^-^a.}

Это даёт нам вероятность того, что y = 1. Чтобы получился прогноз, мы просто округляем полученную вероятность.

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

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

z_j = \sigma (\sum {}_i (W_i_j \;x_i) + b_j),

p(y|x) = \sigma (\sum_j (v_j z_j) + c).

Заметьте, что W теперь является матрицей, так как теперь нам нужны весовые коэффициенты для каждой пары входных и исходящих данных. Поэтому если, к примеру, есть две входящие переменные и три исходящие, то в итоге у нас будет шесть весовых коэффициентов. Обратите внимание, что каждый узел z имеет собственный свободный член bj.

Теперь давайте поговорим о нелинейности. Это именно то, что делает нейронные сети столь мощными, ведь они являются нелинейными классификаторами.

Мы уже знакомы с сигмоидой:

\sigma (x) = \frac{1}{1 + e^-^x.}

Кратко говоря, это S-образная кривая, имеющая значения функции в диапазоне от 0 до 1.

Другой популярной нелинейной функцией является гиперболический тангенс:

tanh (x) = \frac{e^x - e ^-^x}{e^x + e^-^x.},

имеющий значения функции в диапазоне от -1 до 1.

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

И наконец, у нас есть нелинейная функция relu(x). Она возвращает нуль при всех значениях аргумента, меньших нуля, и значение аргумента во всех остальных функциях:

relu (x) = max (0, \;x).

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

Разберём теперь численный пример. Пусть наши входные переменные имеют значения x1 = 1, x2 = 0; то же самое мы можем выразить в векторной форме, установив значение весовых коэффициентов, равными единице, а значение свободного члена – равным нулю. Вначале нам надо вычислить значения z. Получим:

z_1 = \sigma (0*1+1*1) = 0.731,

z_2 = \sigma (0*1+1*1) = 0.731.

Значения обоих z равны, так как они обе в качестве входных переменных имеют 0 и 1 с одинаковыми весовыми коэффициентами.

Далее мы вычисляем Y, что даёт нам значение около 0,8:

p (y|x) = \sigma (0.731*1+0.731*1) = 0.812.

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

Не забывайте, что при использовании библиотеки Numpy всегда быстрее применять встроенные векторные и матричные операторы, нежели использовать обычные операторы цикла Python. Мы можем рассматривать наши x как вектор размерности D, а наши z – как вектор размерности M. В таком случае для вычисления результата работы нейронной сети мы использовать запись в векторной форме:

z_j = \sigma (\sum {}_i (W_i_j \;x_i) + b_j) \rightarrow z_j = \sigma(W^T_j x +b_j) \rightarrow z = \sigma (W^T x + b),

p (y|x) = \sigma (\sum {}_j (v_j z_j) + c) \rightarrow p (y|x) = \sigma (v^T z + c).

Мы даже можем пойти ещё дальше. Не забывайте, что у нас ожидается не одна единица входных данных, а множество их, а мы хотим, чтобы нейронная сеть одновременно корректно работала со всеми из них. Поэтому мы можем далее перевести наши вычисления в векторную форму, используя полную матрицу входных данных – это будет матрица размерности NxD, где N – количество примеров (наблюдений), а D – размерность входных данных. Поскольку мы будем вычислять всё одновременно, то z будет представлять собой матрицу размерности NxM, а наше исходящее Y – матрицу размерности Nx1. В случае мультиклассовой классификации, когда у нас будет K классов, наше Y будет представлять уже матрицу размерности NxK. В свою очередь, поскольку все члены должны корректно перемножаться по правилам матричного умножения, все одни должны иметь соответствующие размерности. Таким образом, W должно быть матрицей DxM, первый свободный член b должен иметь размерность Mx1, то есть вектором размерности M, исходящий весовой коэффициент v – иметь размерность Mx1, а исходящий свободный член – скалярной величиной.

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

Построение целой нейронной сети прямого распространения на Python

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

Итак, как обычно, прежде всего мы импортируем библиотеки Numpy и Matplotlib.

import numpy as np

import matplotlib.pyplot as plt

Создадим по 500 примеров на класс.

Nclass = 500

Мы сгенерируем несколько гауссовых облаков, точнее, три гауссовых облака. Первое из них центрировано на координаты (0, -2), второе – на координаты (2, 2), а третье – на координаты (-2, 2).

X1 = np.random.randn(Nclass, 2) + np.array([0, -2])

X2 = np.random.randn(Nclass, 2) + np.array([2, 2])

X3 = np.random.randn(Nclass, 2) + np.array([-2, 2])

X = np.vstack([X1, X2, X3])

Далее создадим метки.

Y = np.array([0]*Nclass + [1]*Nclass + [2]*Nclass)

Мы можем отобразить получившееся графически.

plt.scatter(X[:,0], X[:, 1], c=Y, s=100, alpha=0.5)

plt.show()

Установим значения D = 2, M – это размер скрытого слоя – установим равным 3, и K – это количество классов – также равным 3.

D = 2

M = 3

K = 3

Инициируем теперь весовые коэффициенты. Как мы знаем, W1 должно иметь размерность DxM, b1 – размерность Mx1, W2 – размерность MxK, b2 – размерность Kx1.

W1 = np.random.randn(D, M)

b1 = np.random.randn(M)

W2 = np.random.randn(M, K)

b2 = np.random.randn(K)

Теперь определим функцию прямого распространения нейронной сети, причём в скрытом слое воспользуемся сигмоидой. Аргументами функции являются матрица данных X и значения весовых коэффициентов W1, b1, W2, b2. После этого мы можем рассчитать софтмакс следующего слоя.

def forward(X, W1, b1, W2, b2):

Z = 1 / (1 + np.exp(-X.dot(W1) – b1))

A = Z.dot(W2) + b2

expA = np.exp(A)

Y = expA / expA.sum(axis=1, keepdims=True)

return Y

Теперь напишем функцию для расчёта коэффициента классификации. Её аргументами являются целевая переменная Y и значения прогноза P.

def classification_rate(Y, P):

n_correct = 0

n_total = 0

for i in xrange(len(Y)):

n_total += 1

if Y[i] == P[i]:

n_correct += 1

return float(n_correct) / n_total

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

P_Y_given_X = forward(X, W1, b1, W2, b2)

P = np.argmax(P_Y_given_X, axis=1)

assert(len(P) == len(Y))

И, наконец, выведем на экран значение коэффициента классификации.

print ‘’Classification rate for randomly chosen weights:’’, classification_rate(Y, P)

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

Итак, запустим файл.

Мы видим наши данные – три гауссовых облака. Коэффициент классификации оказался в точности таким, каким и ожидался, – одна третья.

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Share via
Copy link