"Машинное обучение"

Как нейронные сети учатся?

Спускаемся вниз по ландшафту потерь

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

Для этого давайте сначала взглянем на изображение ниже:

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

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

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

Как мы на самом деле узнаем эти параметры, чтобы делать хорошие прогнозы?

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

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

Стохастический градиентный спуск

Возможно, вы знакомы с проблемой поиска минимума функции из вашего класса исчисления. Там вы обычно берете градиент вашей функции, устанавливаете его равным 0, находите все решения (также называемые критическими точками), а затем выбираете среди них то, которое дает вашей функции наименьшее значение. И это глобальный минимум. Можем ли мы сделать то же самое, минимизируя нашу функцию потерь? Не совсем. Проблема в том, что функция потерь нейронной сети не так хороша и компактна, как те, которые вы обычно найдете в учебниках по математическому анализу. Это чрезвычайно сложная функция с тысячами, сотнями тысяч или даже миллионами параметров. Может быть, даже невозможно найти решение этой проблемы в закрытом виде. К этой проблеме обычно подходят итерационные методы, методы, которые не пытаются найти прямое решение, а вместо этого начинают со случайного решения и пытаются немного улучшить его на каждой итерации. В итоге после большого количества итераций мы получим довольно хорошее решение.

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

Где θ (тета) представляет собой вектор, содержащий все параметры сети.

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

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

Где α - неотрицательный фактор, определяющий вклад прошлых градиентов. Когда он равен 0, мы просто не используем импульс.

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

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

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

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

Итак, при обратном распространении, когда мы сталкиваемся с функциями, у которых нет обучаемых параметров (например, функций активации), мы берем производные только первого типа, просто чтобы распространить ошибки в обратном направлении. Но когда мы сталкиваемся с функциями, у которых действительно есть обучаемые параметры (например, линейные комбинации, у нас есть веса и смещения, которые мы хотим изучить), мы берем производные обоих видов: первый - с. его вход для распространения ошибки, а второй - w.r.t. его веса и смещения и сохраните их как часть градиента. Мы выполняем этот процесс, начиная с функции потерь и до тех пор, пока не дойдем до первого слоя, где у нас нет никаких обучаемых параметров, которые мы хотим добавить к градиенту. Это алгоритм обратного распространения ошибки.

Активация Softmax и потеря кросс-энтропии

Часто используемой функцией активации для последнего уровня в задаче классификации является функция softmax.

Функция softmax преобразует свой входной вектор в распределение вероятностей. Если вы посмотрите выше, вы увидите, что элементы вектора вывода softmax обладают положительными свойствами, а их сумма равна 1. Когда мы используем активацию softmax, мы создаем на последнем слое столько узлов, сколько классов в наш набор данных, а активация softmax даст нам распределение вероятностей по возможным классам. Таким образом, выходные данные сети дадут нам вероятность того, что входной вектор принадлежит каждому из возможных классов, и мы выбираем класс, который имеет наибольшую вероятность, и сообщает об этом в качестве прогноза нашей сети.

Когда softmax используется как активация выходного слоя, мы обычно используем в качестве функции потерь кросс-энтропийную потерю. Потери кросс-энтропии измеряют, насколько похожи 2 распределения вероятностей. Мы можем представить истинную метку нашего входного x как распределение вероятностей: такое, в котором у нас есть вероятность 1 для истинной метки класса и 0 для других меток класса. Такое представление меток также называется горячим кодированием. Затем мы используем кросс-энтропию, чтобы измерить, насколько близко предсказанное распределение вероятностей нашей сети к истинному.

Где y - быстрое кодирование истинной метки, y hat - предсказанное распределение вероятностей, а yi, yi hat - элементы этих векторов.

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

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

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

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

Надеюсь, эта информация была для вас полезной, и спасибо за внимание!

Эта статья также размещена на моем собственном сайте здесь. Не стесняйтесь смотреть!