Машинное обучение стало неотъемлемой частью нашей повседневной жизни. Машинное обучение позволяет использовать современные беспилотные автомобили, помогает прогнозировать сердечные заболевания и многое другое. Даже самые популярные фильтры Snapchat построены на алгоритмах распознавания лиц. Кроме того, разработка моделей машинного обучения становится все проще с помощью таких простых в использовании инструментов, как scikit-learn, Tensorflow, Keras и обучаемая машина Google.

Одно дело использовать перечисленные выше инструменты, другое - понимать, что они делают. Я хотел понять, что происходит под капотом, пожалуй, самого популярного алгоритма машинного обучения, нейронной сети (в частности, многослойного перцептрона). Вот почему я решил разработать его с нуля, используя базовый Python и numPy.

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

Итак, приступим!

Обзор нейронной сети

Сначала мне нужно было получить широкое представление о том, что такое нейронная сеть и как она «учится». Итак, сначала давайте опишем архитектуру нейронной сети. Рассмотрим сеть ниже.

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

Чтобы понять, как нейронная сеть «учится», я посмотрел серию статей о нейронных сетях от 3Blue1Brown, которые я настоятельно рекомендую вам посмотреть самому. Однако я постараюсь объяснить здесь.

По сути, цель нейронной сети - минимизировать некоторую функцию затрат. Функция стоимости - это, по сути, способ количественной оценки того, насколько неверно ваше предсказанное значение. Вы прогнозируете значения с помощью алгоритма, называемого прямой связью, который будет описан в этой статье. Затем на основе ошибки предсказанного значения вы настраиваете сеть, чтобы сделать ее более точной, используя градиентный спуск и обратное распространение, которые будут объяснены в следующих статьях. Если вы хотите получить более подробное объяснение, обязательно посмотрите серию статей о нейронных сетях 3Blue1Brown здесь.

В этой статье мы рассмотрим алгоритм прямой связи.

Как работает Feedforward

Суть прямой связи состоит в том, чтобы переместить входы нейронной сети в выходы. Это делается с помощью серии матричных операций. Давайте посчитаем, каким будет выход нейрона H1.

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

Переменные, обозначенные I и w, четко отображаются в сети. Однако переменная b новая. Это называется смещением, заданным конкретному нейрону. Цель смещения - обеспечить своего рода смещение, а также помогает уменьшить переоснащение. Если вы хотите лучше объяснить, что именно влечет за собой переоснащение, вы можете прочитать здесь, а для лучшего объяснения того, в чем заключается предвзятость, смотрите здесь.

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

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

Итак, теперь давайте рассмотрим, как я реализовал это в Python.

Прямая реализация

Сначала я создал класс Layer, который принимает выходные размеры, которые в основном представляют собой количество нейронов в слое.

import numpy as np
class Layer():
   def __init__(self, output_dim):
       self.output_dim = output_dim
       self.input_dim = 0
       self.activations = np.array([])
       self.weights = np.random.randn(self.output_dim,         self.input_dim)
       self.biases = np.zeros(output_dim)

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

def update_dim(self, input_dim):
   self.input_dim = input_dim
   self.weights = np.random.randn(self.output_dim, self.input_dim)

Теперь давайте создадим подкласс под названием InputLayer, унаследованный от Layer. Когда вы создаете InputLayer, вы указываете входное измерение и просто делаете выходное измерение таким же, как входное. И, наконец, сделайте переменную для активаций.

class InputLayer(Layer):
     def __init__(self, input_dim):
         self.input_dim = input_dim
         self.output_dim = input_dim
         self.activations = np.array([])

Затем я добавил метод под названием feedforward, который принимает в качестве входных данных предыдущий слой в сети.

def feedforward(self, input_layer):
    input_activations = input_layer.activations 
    dot_product = np.dot(self.weights, input_activations)
    activations = np.add(dot_product, self.biases)
    self.activations = sigmoid(activations)

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

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

class NeuralNetwork():
    def __init__(self):
        self.layers = []

Теперь давайте создадим метод добавления слоя в сеть.

def add_layer(self, layer):
    if len(self.layers) > 0:
        input_dim = self.layers[len(self.layers)-1].output_dim
        layer.update_dim(input_dim)
 
    self.layers.append(layer)

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

Теперь давайте создадим метод прямой связи для нейронной сети.

def feedforward(self, inputs):
    self.layers[0].activations = inputs
    for i in range(1, len(self.layers)):
        layer_i = self.layers[i]
        layer_i.feedforward(self.layers[i-1])
 
    output = self.layers[len(self.layers)-1].activations
 
    return output

Метод прямой связи для нейронной сети принимает параметр, называемый входными данными. В целом это похоже на единичный экземпляр обучающих данных. Во-первых, активации InputLayers устанавливаются на значение input. Затем мы перебираем слои, кроме InputLayer. Для каждого слоя мы продвигаем предыдущий слой. Конечный результат - это активации последнего слоя.

Давайте убедимся, что это работает, быстро протестировав его.

network = NeuralNetwork()
network.add_layer(InputLayer(3))
network.add_layer(Layer(2))
network.add_layer(Layer(1))
result = network.feedforward(np.array([1, 2, 3]))
print(result)
>>> [.86837953]

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

Другие материалы и источники:





«Что такое переоснащение и неполное оснащение в машинном обучении?
Когда вы входите в сферу машинного обучения, вам будут встречаться несколько двусмысленных терминов. Такие термины, как "Переоборудование… в сторонуdatascience.com"