Этот пост является частью серии Стажировка в Hasura и посвящен настройке Hausra для местного развития. В дополнение к этому мы, наконец, видим, как нейронная сеть обучается. Также ознакомьтесь с моими предыдущими сообщениями: Часть 1, Часть 2, Часть 3, Часть 4, где представлена ​​идея приложения и некоторые основы компьютерного зрения и нейронных сетей.

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

Шаг 1: Установите virtualbox. VirtualBox - бесплатное кроссплатформенное приложение с открытым исходным кодом для создания и запущенные виртуальные машины (ВМ) - компьютеры, аппаратные компоненты которых эмулируются главным компьютером, компьютером, на котором выполняется программа. Он позволяет устанавливать на нем дополнительные операционные системы в качестве гостевой ОС и запускать их в виртуальной среде. Важно отметить, что на главном компьютере должно быть не менее 4 ГБ ОЗУ (поскольку виртуальная машина может занимать до 2 ГБ ОЗУ). Также убедитесь, что у вас 64-битная ОС.

Шаг 2. Установите hasuractl. Команда для его установки в моей системе (Mac) была:

curl -Lo hasuractl https://storage.googleapis.com/hasuractl/v0.1.2/darwin-amd64/hasuractl && chmod +x hasuractl && sudo mv hasuractl /usr/local/bin/

Шаг 3: Установите kubectl.

Чтобы начать работу над проектом на Hasura, создайте учетную запись на beta.hasura.io и затем выполните следующую команду:

hasuractl login

После входа в систему выполните следующую команду (Примечание: если вы запускаете следующую команду в первый раз, она загрузит примерно 1–1,5 ГБ образов докеров.):

hasuractl local start

Дополнительные команды для остановки и удаления проектов Hasura:

hasuractl local stop      ## To stop the running hasura platform.
hasuractl local clean     ## To clean up the incomplete setup.
hasuractl local delete    ## To delete the underlying VM

Давайте быстро погрузимся в Обратное распространение и Градиентный спуск. В части 4 мы увидели, как NN делает прогноз во время того, что мы называем прямым распространением. Мы прогнозировали поступление студента в университет на основе его / ее предыдущего балла. Теперь, когда у нас есть прогноз, как узнать, верен он или нет, и насколько мы близки к правильному ответу. Это то, что происходит во время тренировки или обновления этих весов, чтобы делать прогнозы.

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

Показатель ошибок, который я использовал здесь, известен как сумма квадратов ошибок (SSE). Я решил выбрать это (есть и другие функции потерь), потому что квадрат гарантирует, что ошибка всегда положительна, и большие ошибки наказываются больше, чем меньшие ошибки. Кроме того, это делает математику приятной и менее пугающей. Здесь f (x) - прогноз, а y - истинное значение, а затем мы суммируем все точки данных i. Это также имеет смысл, поскольку в конечном итоге мы хотим выяснить, насколько плохи наши прогнозы на основе правильного ответа. Это означает, что если наша нейронная сеть не работает, эта «ошибка» будет большой - это будет означать, что f (x) не близок к выходному y для большого количества точек данных. Более того, если стоимость (ошибка) становится небольшой, т. Е. SSE (f) ≈0, именно тогда, когда y приблизительно равно предсказанию, f (x ) для всех обучающих входов i, мы можем сделать вывод, что NN проделала хорошую работу. Таким образом, цель нашего обучающего алгоритма - минимизировать эту «ошибку» как функцию весов и смещений. Другими словами, мы хотим найти набор весов и смещений, которые сделают эту «ошибку» как можно меньше. Мы сделаем это с помощью алгоритма, известного как градиентный спуск.

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

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

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

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

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

Найдем производную функции f (x). Возьмем простую функцию f (x) = . Производная даст нам другую функцию f ′ (x), которая возвращает наклон f (x ) в точке x. Производная от x ² равна f ′ (x) = 2 x. Итак, при x = 2 наклон равен f ′ (2) = 4. Это выглядит так:

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

Вес будет обновлен как:

W + = n * дельта

где n называется «скоростью обучения», дельта - это произведение ошибки (yf (x)) и производной функции активации (f '(x)) . Градиент сообщает нам направление, в котором функция имеет наибольшую скорость увеличения, но не говорит нам, как далеко в этом направлении мы должны шагнуть. В качестве константы принимается скорость обучения ( или размер шага), которая является одной из самых важных настроек гиперпараметров при обучении нейронной сети.

Мы снова используем для этого numpy:

import numpy as np
## Sigmoid (Activation) function
def sigmoid(x):                    
    return 1/(1+np.exp(-x))
## Derivative of the Sigmoid (Activation) function
def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))
## Grades for single student in 4 subjects i.e only 1 data point
inputs = np.array([50, 22, 10, 45])
## Correct answer (1 : admitted, 0: not admitted) 
y = np.array([1])        
## Initialise the weights and bias randomly
initial_weights = np.array([0.01, 0.8, 0.02, -0.7])
bias = -0.1
## Set a value for learning rate
learning_rate = 0.001
## Our Prediction (f(x))
output = sigmoid(np.dot(weights, inputs) + bias)
## Calculate the error i.e how incorrect are we
error = y - output
delta = error * sigmoid_derivative(output)
# Gradient descent step
change_in_weights = learning_rate * delta * inputs
## Updating our weights
new_weights = initial_weights + change_in_weights
print ('Initial Weights: {}'.format(initial_weights))
print('Our prediction: {}'.format(output))
print('Amount of Error: {}'.format(error))
print('Change in Weights: {}'.format(change_in_weights))
print('New weights: {}'.format(new_weights))

Вывод :

Initial Weights: [ 0.01  0.8   0.02 -0.7 ] 
Our prediction: 1.6744904055114616e-06 
Amount of Error: [ 0.99999833] 
Change in Weights: [ 0.01249998  0.00549999  0.0025      0.01124998] New weights: [ 0.02249998  0.80549999  0.0225  -0.68875002]

Хотя всегда полезно понимать концепции, лежащие в основе обратного распространения ошибки, но если вам трудно понять математику, все в порядке. Библиотеки машинного обучения и глубокого обучения, которые мы используем (scikit-learn, Tensorflow и т. Д.), Имеют встроенные инструменты, позволяющие рассчитать все за вас.

(Изменить: пожалуйста, сообщайте о любых ошибках или неточностях в комментариях, или вы можете связаться со мной: [email protected])