в Статьи

Как построить трехслойную нейронную сеть с нуля на Python

Spread the love

В этой статье мы рассмотрим шаги, необходимые для построения трехслойной нейронной сети. Будет рассмотрена проблема и объяснен процесс вместе с наиболее важными концепциями.

Проблема, которую нужно решить

У итальянского фермера были проблемы с его этикетировочной машиной: она перепутала этикетки трех сортов вина. Теперь у него осталось 178 бутылок, и никто не знает, какого они сорта! Чтобы помочь этому бедному человеку, мы создадим классификатор, который распознает вино на основе 13 атрибутов.

Тот факт, что наши данные помечены (одной из трех меток сорта), делает эту проблему Supervised learning. По сути, мы хотим использовать наши входные данные (178 не секретных винных бутылок) для обучения нейронной сети, а затем получим правильную метку для каждого сорта вина в качестве результата.

Нейронная сеть будет тренироваться, чтобы лучше предсказать (y-hat), какая бутылка принадлежит какой этикетке.

Теперь пришло время начать строить нейронную сеть!

Нейронная сеть: метод построения

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

Overview of the 3 Layer neural network, a wine classifier

Overview of the 3 Layer neural network, a wine classifier

Вкратце:

Входной слой (х) состоит из 178 нейронов.
А1, первый слой, состоит из 8 нейронов.
А2, второй слой, состоит из 5 нейронов.
А3, третий и выходной слой, состоит из 3 нейронов.

Шаг 1: обычная подготовка

Импортируйте все необходимые библиотеки (NumPy, skicit-learn, pandas) и набор данных и определите x и y.

 

#importing all the libraries and dataset
import pandas as pd
import numpy as np
df = pd.read_csv('../input/W1data.csv')
df.head()
# Package imports
# Matplotlib
import matplotlib
import matplotlib.pyplot as plt
# SciKitLearn is a machine learning utilities library
import sklearn
# The sklearn dataset module helps generating datasets
import sklearn.datasets
import sklearn.linear_model
from sklearn.preprocessing import OneHotEncoder
from sklearn.metrics import accuracy_score


Шаг 2: инициализация

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

В Python функция random.seed генерирует «случайные числа». Однако случайные числа не являются действительно случайными. Сгенерированные числа являются псевдослучайными, то есть числа генерируются сложной формулой, которая делает их случайными. Для генерации чисел формула принимает предыдущее значение, сгенерированное в качестве входных данных. Если предыдущее значение не генерируется, часто в качестве первого значения используется время.

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

np.random.seed(0)


Шаг 3: прямое распространение (forward propagation)

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

После того, как мы инициализировали веса псевдослучайным числом, мы делаем линейный шаг вперед. Мы рассчитываем это, взяв наше входное значение A0, умноженное на произведение случайных инициализированных весов плюс смещение. Мы начали с смещения 0. Оно представляется как:

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

Существуют различные виды функций активации. Для этой модели мы решили использовать функцию активации tanh для наших двух скрытых слоев — A1 и A2. Функция дает нам выходное значение от -1 до 1.

Так как это проблема классификации нескольких классов (у нас есть 3 метки), мы будем использовать функцию softmax для выходного слоя — A3 — потому что она вычислит вероятности для классов, на выходе будет значение между 0 и 1.

tanh function

tanh function

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

В Python этот процесс выглядит так:

 

# This is the forward propagation function
def forward_prop(model,a0):

# Load parameters from model
W1, b1, W2, b2, W3, b3 = model['W1'], model['b1'], model['W2'], model['b2'], model['W3'],model['b3']

# Do the first Linear step
z1 = a0.dot(W1) + b1

# Put it through the first activation function
a1 = np.tanh(z1)

# Second linear step
z2 = a1.dot(W2) + b2

# Put through second activation function
a2 = np.tanh(z2)

#Third linear step
z3 = a2.dot(W3) + b3

#For the Third linear activation function we use the softmax function
a3 = softmax(z3)

#Store all results in these values
cache = {'a0':a0,'z1':z1,'a1':a1,'z2':z2,'a2':a2,'a3':a3,'z3':z3}
return cache

В итоге все наши значения хранятся в кеше.

Шаг 4: обратное распространение (back propagation)

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

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

Давайте визуализируем этот процесс по аналогии.

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

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

Итак, пошли, пошагово осторожно спускаясь вниз. Теперь думайте о горе как о функции потерь, и вы — алгоритм, пытающийся найти свой дом (то есть самую низкую точку). Каждый раз, когда вы делаете шаг вниз, мы обновляем координаты вашего местоположения (алгоритм обновляет параметры).

Функция потерь представлена ​​горой. Чтобы достичь низких потерь, алгоритм следует наклону — то есть производной — функции потерь.

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

На самом деле градиентный спуск выглядит примерно так:

Мы всегда начинаем с вычисления наклона функции потерь относительно z, наклона линейного шага, который мы делаем.

Обозначения следующие: dv — производная функции потерь по переменной v.

Затем мы вычисляем наклон функции потерь относительно наших весов и смещений. Поскольку это слой 3 уровня, мы повторим этот процесс для z3,2,1 + W3,2,1 и b3,2,1. Распространение в обратном направлении от выхода к входному слою.

Вот как этот процесс выглядит в Python:

 

# This is the backward propagation function
def backward_prop(model,cache,y):
# Load parameters from model
W1, b1, W2, b2, W3, b3 = model['W1'], model['b1'], model['W2'], model['b2'],model['W3'],model['b3']

# Load forward propagation results
a0,a1, a2,

a3 = cache['a0'],cache['a1'],cache['a2'],cache['a3']

# Get number of samples
m = y.shape[0]

# Calculate loss derivative with respect to output
dz3 = loss_derivative(y=y,y_hat=a3)
# Calculate loss derivative with respect to second layer weights
dW3 = 1/m*(a2.T).dot(dz3) #dW2 = 1/m*(a1.T).dot(dz2)

# Calculate loss derivative with respect to second layer bias
db3 = 1/m*np.sum(dz3, axis=0)

# Calculate loss derivative with respect to first layer
dz2 = np.multiply(dz3.dot(W3.T) ,tanh_derivative(a2))

# Calculate loss derivative with respect to first layer weights
dW2 = 1/m*np.dot(a1.T, dz2)

# Calculate loss derivative with respect to first layer bias
db2 = 1/m*np.sum(dz2, axis=0)

dz1 = np.multiply(dz2.dot(W2.T),tanh_derivative(a1))

dW1 = 1/m*np.dot(a0.T,dz1)

db1 = 1/m*np.sum(dz1,axis=0)

# Store gradients
grads = {'dW3':dW3, 'db3':db3, 'dW2':dW2,'db2':db2,'dW1':dW1,'db1':db1}
return grads

Шаг 5: этап обучения

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

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

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

Таким же образом, вы должны указать «скорость обучения» для модели. Скорость обучения является множителем для обновления параметров. Он определяет, как быстро они могут измениться. Если скорость обучения низкая, обучение займет больше времени. Однако, если скорость обучения слишком высока, мы можем пропустить минимум. Скорость обучения выражается как:

  • : = означает, что это определение, а не уравнение или проверенное утверждение.
  • скорость обучения называется альфа
  • dL (w) — это производная от общей потери по отношению к нашему весу w
  • da является производной альфа

Мы выбрали скорость обучения 0,07 после некоторых экспериментов.

# This is what we return at the end
model = initialise_parameters(nn_input_dim=13, nn_hdim= 5, nn_output_dim= 3)
model = train(model,X,y,learning_rate=0.07,epochs=4500,print_loss=True)
plt.plot(losses)

Наконец, это наш график. Вы можете построить график со своей точностью и / или потерями. После 4500 эпох наш алгоритм имеет точность 99,4382022472%.

Короткое повторение

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

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

Затем мы вычисляем разницу между нашим прогнозом (y-hat) и ожидаемым выходным значением (y) и используем это значение ошибки во время обратного распространения.

Во время обратного распространения мы принимаем нашу ошибку — разницу между нашим прогнозом y-hat и y — и математически проталкиваем ее обратно через слой в другом направлении. Мы учимся на своих ошибках.

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

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

Overview of Forward and Backwards propagation

Overview of Forward and Backwards propagation

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

Переведено по материалам Daphne Cornelisse. How to build a three-layer neural network from scratch

Оставить комментарий

Комментарии