Принцип алгоритма BP в нейронной сети и анализ исходного кода реализации Python

Python алгоритм Нейронные сети

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

содержание

  • Что такое градиентный спуск и цепной вывод
  • Структура нейронной сети
  • Поток выполнения в алгоритме BP (прямой проход и обратное обновление)
  • Получение выходных и скрытых весов слоев и обновлений смещения
  • Python реализует анализ исходного кода
  • Пример распознавания рукописных цифр
  • В чем сложности обучения нейронных сетей (TODO)

Градиентный спуск и цепной вывод

Предположим, у нас есть функция J(w), как показано ниже.

Принципиальная схема градиентного спуска

Теперь спросим, ​​когда w равно чему-то, J(w) может принимать минимальное значение. Из рисунка мы знаем, что минимальное значение находится слева от начального положения, а это означает, что если мы хотим минимизировать J(w), значение w необходимо уменьшить. А наклон касательной в начальном положении a > 0 (то есть производная, соответствующая этому положению, больше 0), w = w — a может уменьшать значение w и циклически выводить и обновлять w до тех пор, пока J( w) достигает минимального значения. Если функция J(w) содержит несколько переменных, то необходимо брать частные производные разных переменных для обновления значений разных переменных.

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

правило вывода цепи

На примере будет понятнее:


Пример получения цепочки

Структура нейронной сети

Нейронная сеть состоит из трех частей: крайнего левого входного слоя, скрытого слоя (которого в практических приложениях гораздо больше, чем один слой) и крайнего правого выходного слоя. Слои соединены линиями, и каждая линия соединения имеет соответствующее значение веса w. В дополнение к входному слою, как правило, каждый нейрон имеет соответствующее смещение b

Структурная схема нейронной сети

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

Формула расчета выходного значения нейрона a

Среди них переменные l и j в формуле представляют j-й нейрон в l-м слое, ij представляет связь от i-го нейрона к j-му нейрону, w представляет вес, b указывает смещение, и значения этих последних символов в целом аналогичны описанным здесь, поэтому они не будут объясняться снова.Следующая Gif-анимация может прояснить, как вычисляются входные и выходные значения каждого нейрона (обратите внимание, что анимация здесь не добавляет смещения, но обычно добавляется при использовании)

Анимированный график, показывающий вычисленные выходные значения нейрона

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

Относительно того, почему модель линейной функции недостаточно выразительна, это может бытькликните сюдаСмотрите обсуждение на Zhihu

сигмовидная функция

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


Процесс выполнения алгоритма BP

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

Среди них условиями остановки могут быть следующие три

  • Когда веса обновляются ниже определенного порога
  • Прогнозируемая частота ошибок ниже определенного порога
  • Достичь заданного количества итераций

Например, при распознавании рукописных цифр изображение рукописной цифры 1 хранит 28 * 28 = 784 пикселя, и каждый пиксель хранит значение серого (диапазон значений составляет [0,255]), тогда это означает, что в качестве нейрона используются 784 нейрона. входной слой, а выходной слой имеет 10 нейронов, представляющих число 09 каждый нейрон принимает значение 01, представляет вероятность того, что на картинке указан этот номер.

Для каждого входного изображения (т. наибольшее значение.

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

Прямая передача (Feed-Forward)

Из входного слоя => скрытого слоя => выходного слоя происходит процесс вычисления выходных значений всех нейронов послойно.

Обратное распространение

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

среднеквадратическая ошибка

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

Обновление формул для весов и смещений

Величина обновления w и b может быть получена путем нахождения частичного вывода w и b для целевой функции E, а ниже приведен вывод нахождения частичного вывода w

Среди них η — это скорость обучения, и значение обычно составляет 0,1 ~ 0,3, что можно понимать как шаг, предпринимаемый каждым градиентом. Обратите внимание, что значение w_hj сначала влияет на входное значение a нейрона j-го выходного слоя, а затем влияет на выходное значение y. Согласно правилу вывода цепочки, мы имеем

Используйте цепное правило, чтобы расширить частную производную весов

В соответствии с определением выходного значения нейрона a имеем

найти частную производную от w по функции z

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

Вывод сигмовидной функции

так

Тогда количество обновлений веса w равно

Точно так же количество обновления b может быть получено как

Однако эти две формулы могут обновлять только вес линии связи между выходным слоем и предыдущим слоем и смещение выходного слоя, поскольку значение δ зависит от переменной y, а мы знаем только истинное значение выходного слоя и не знаю. Реальное значение каждого скрытого слоя делает невозможным вычисление значения δ каждого скрытого слоя, поэтому мы надеемся, что сможем использоватьl+1дельта-значение слоя для расчетаlДельта-значение слоя, а это можно сделать после ряда математических преобразований, откуда и пошло название обратной обратной связи, формула выглядит следующим образом:

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

Перед выводом обратите внимание на следующую картину

нейроны в слоях l и l+1

Сначала мы видимlпервоеiнейроны сl+1Все нейроны в слое связаны, тогда мы можем разложить δ в следующую формулу

То есть мы можем рассматривать E какl+1входные значения всех нейронов слояzфункция, а приведенная выше формулаnЗначит этоl+1Количество нейронов в слое можно упростить, чтобы получить приведенную выше формулу.

Процесс вывода здесь объясняет только ключевые части,Если вы хотите увидеть более подробный вывод, вы можете щелкнуть здесь, чтобы загрузить те, на которые я ссылался в процессе обучения.документ в формате pdf, процесс вывода внутри очень подробный. Кроме того, я также ссылаюсь на содержание части нейронной сети в машинном обучении, написанной Чжоу Чжихуа иneuralnetworksanddeeplearningСодержание


Анализ исходного кода

исходный код изMichael NielsenОнлайн-учебник по глубокому обучению Великого Бога, но его содержаниеанглийскийДа, я аннотировал исходный код, основываясь на собственном понимании и приведенных выше теоретических знаниях.>>Нажмите здесь, чтобы просмотреть сопоставленные коды и примеры распознавания чисел

Количество строк кода нейронной сети, реализованной на Python, невелико, и она содержит только класс Network, для начала рассмотрим метод построения этого класса.

def __init__(self, sizes):
        """
        :param sizes: list类型,储存每层神经网络的神经元数目
                      譬如说:sizes = [2, 3, 2] 表示输入层有两个神经元、
                      隐藏层有3个神经元以及输出层有2个神经元
        """
        # 有几层神经网络 
        self.num_layers = len(sizes)
        self.sizes = sizes
        # 除去输入层,随机产生每层中 y 个神经元的 biase 值(0 - 1)
        self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
        # 随机产生每条连接线的 weight 值(0 - 1)
        self.weights = [np.random.randn(y, x)
                        for x, y in zip(sizes[:-1], sizes[1:])]

Код для FreedForward

    def feedforward(self, a):
        """
        前向传输计算每个神经元的值
        :param a: 输入值
        :return: 计算后每个神经元的值
        """
        for b, w in zip(self.biases, self.weights):
            # 加权求和以及加上 biase
            a = sigmoid(np.dot(w, a)+b)
        return a

Используемый исходный кодСтохастический градиентный спуск (SGD), принцип аналогичен градиентному спуску, за исключением того, что алгоритм стохастического градиентного спуска берет только часть набора данных на каждой итерации для обновления значений w и b, что быстрее, чем градиентный спуск, но он делает не обязательно сходятся к локальному минимальному значению, которое может колебаться вокруг локальных минимумов.

    def SGD(self, training_data, epochs, mini_batch_size, eta,
            test_data=None):
        """
        随机梯度下降
        :param training_data: 输入的训练集
        :param epochs: 迭代次数
        :param mini_batch_size: 小样本数量
        :param eta: 学习率 
        :param test_data: 测试数据集
        """
        if test_data: n_test = len(test_data)
        n = len(training_data)
        for j in xrange(epochs):
            # 搅乱训练集,让其排序顺序发生变化
            random.shuffle(training_data)
            # 按照小样本数量划分训练集
            mini_batches = [
                training_data[k:k+mini_batch_size]
                for k in xrange(0, n, mini_batch_size)]
            for mini_batch in mini_batches:
                # 根据每个小样本来更新 w 和 b,代码在下一段
                self.update_mini_batch(mini_batch, eta)
            # 输出测试每轮结束后,神经网络的准确度
            if test_data:
                print "Epoch {0}: {1} / {2}".format(
                    j, self.evaluate(test_data), n_test)
            else:
                print "Epoch {0} complete".format(j)

Обновите значения w и b в соответствии с частными производными, полученными методом обратного распространения

    def update_mini_batch(self, mini_batch, eta):
        """
        更新 w 和 b 的值
        :param mini_batch: 一部分的样本
        :param eta: 学习率
        """
        # 根据 biases 和 weights 的行列数创建对应的全部元素值为 0 的空矩阵
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        for x, y in mini_batch:
            # 根据样本中的每一个输入 x 的其输出 y,计算 w 和 b 的偏导数
            delta_nabla_b, delta_nabla_w = self.backprop(x, y)
            # 累加储存偏导值 delta_nabla_b 和 delta_nabla_w 
            nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
            nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
        # 更新根据累加的偏导值更新 w 和 b,这里因为用了小样本,
        # 所以 eta 要除于小样本的长度
        self.weights = [w-(eta/len(mini_batch))*nw
                        for w, nw in zip(self.weights, nabla_w)]
        self.biases = [b-(eta/len(mini_batch))*nb
                       for b, nb in zip(self.biases, nabla_b)]

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

    def backprop(self, x, y):
        """
        :param x:
        :param y:
        :return:
        """
        nabla_b = [np.zeros(b.shape) for b in self.biases]
        nabla_w = [np.zeros(w.shape) for w in self.weights]
        # 前向传输
        activation = x
        # 储存每层的神经元的值的矩阵,下面循环会 append 每层的神经元的值
        activations = [x] 
        # 储存每个未经过 sigmoid 计算的神经元的值
        zs = [] 
        for b, w in zip(self.biases, self.weights):
            z = np.dot(w, activation)+b
            zs.append(z)
            activation = sigmoid(z)
            activations.append(activation)
        # 求 δ 的值
        delta = self.cost_derivative(activations[-1], y) * \
            sigmoid_prime(zs[-1])
        nabla_b[-1] = delta
        # 乘于前一层的输出值
        nabla_w[-1] = np.dot(delta, activations[-2].transpose())
        for l in xrange(2, self.num_layers):
            # 从倒数第 l 层开始更新,-l 是 python 中特有的语法表示从倒数第 l 层开始计算
            # 下面这里利用 l+1 层的 δ 值来计算 l 的 δ 值
            z = zs[-l]
            sp = sigmoid_prime(z)
            delta = np.dot(self.weights[-l+1].transpose(), delta) * sp
            nabla_b[-l] = delta
            nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
        return (nabla_b, nabla_w)

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

    def evaluate(self, test_data):
        # 获得预测结果
        test_results = [(np.argmax(self.feedforward(x)), y)
                        for (x, y) in test_data]
        # 返回正确识别的个数
        return sum(int(x == y) for (x, y) in test_results)

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

import mnist_loader
import network

training_data, validation_data, test_data = mnist_loader.load_data_wrapper()
net = network.Network([784, 30, 10])
net.SGD(training_data, 30, 10, 3.0, test_data = test_data)
# 输出结果
# Epoch 0: 9038 / 10000
# Epoch 1: 9178 / 10000
# Epoch 2: 9231 / 10000
# ...
# Epoch 27: 9483 / 10000
# Epoch 28: 9485 / 10000
# Epoch 29: 9477 / 10000

Видно, что после 30 раундов итераций точность нейросети для распознавания рукописного текста составляет около 95%.Конечно, установка разных итераций, скорость обучения и количество выборок будут влиять на точность.Как настроить параметры также техника Live, эта дыра будет заполнена позже.

Суммировать

Преимущества нейронных сетей:

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

Недостатки нейронных сетей:

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

Области применения:

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


TODO

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

  1. Как подобрать значения гиперпараметров, таких как количество слоев в нейронной сети и количество нейронов в каждом слое и скорость обучения
  2. Поскольку он чувствителен к весу инициализации, как его избежать и исправить
  3. Сигмовидная функция активации столкнется с проблемой исчезновения градиента в глубокой нейронной сети, как ее решить
  4. Что такое регуляризация L1 и L2, чтобы избежать переобучения

Ссылаться на

[1] Машинное обучение Чжоу Чжихуа
[2] Онлайн-курс Стэнфордского университета по машинному обучению
[[3] Parallel Distributed Processing (1986, by David E. Rumelhart, James L. McClelland), Chapter 8 Learning Internal Representations by Error Propagation](psych.stanford. edu/~jlm/papers/PDP/Volume%201/Chap8_PDP86.pdf)
[[4] How the backpropagation algorithm works](neuralnetworksanddeeplearning.com/chap2. html)
[[5] Backpropagation Algorithm](глубокое обучение.Stanford.quote/wiki/index. … Algorithm)
[6] Цепное правило вывода, цифровой курс Китайского университета науки и технологий на Тайване, видео на Youtube, кстати, нужно пройти через стену, amway посмотреть их видео, связанные с математикой, потому что они очень простые понять