Недавно я начал работать над новой функцией для Shogun-toolbox, которая представляет собой бесплатную программную библиотеку для машинного обучения с открытым исходным кодом, написанную на C ++. Он предлагает множество алгоритмов и структур данных для задач машинного обучения. Он предлагает интерфейсы для Octave, Python, R, Java, Lua, Ruby и C # [1].

Чтобы понять, что я буду объяснять в этом наборе руководств (и что я буду делать в Shogun), мы сначала обратимся к другим известным фреймворкам машинного обучения и глубокого обучения.

Итак, если вы когда-либо использовали Tensorflow или любую из известных библиотек глубокого обучения, вы могли видеть код, который выглядит так:

# Обратное распространение
cost = tf.reduce_mean (tf.nn.softmax_cross_entropy_with_logits (labels = y, logits = yhat))
updates = tf.train.GradientDescentOptimizer (0.01) .minimize (cost)

Сначала функция стоимости определяется как функция переменных (в случае нейронной сети это веса нейронной сети), а затем вы пытаетесь минимизировать эту функцию стоимости, используя некоторый алгоритм (некоторые примеры: AdamOptimizer, GradientDescentOptimizer ,…).

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

Типы компьютерной дифференциации

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

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

Теперь есть три способа сделать это для компьютера:

Системы компьютерной алгебры:

Система компьютерной алгебры (CAS) - это любое математическое программное обеспечение, способное манипулировать математическими выражениями аналогично традиционным ручным вычислениям математиков и ученых. [2] Так что в нашем случае это вычислил бы две необходимые частные производные, аналогично тому, как это сделал бы человек, с большим количеством алгебры и шагов. Однако этот метод не совсем используется в большинстве библиотек, так как CAS, как правило, должен быть достаточно продвинутым.

Числовая дифференциация

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

Следовательно, поскольку мы знаем, что h приближается к 0, мы можем взять очень маленькое значение h, скажем, h = 0,0000000001, и подставить его в приведенные выше уравнения, чтобы аппроксимировать частные производные.

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

Наконец, автоматическая дифференциация

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

Существует два типа автоматического дифференцирования: обратный режим и прямой режим. Мы не будем здесь много говорить о них, но стоит отметить, что есть два типа AD.

Как выглядит автоматическая дифференциация? (СТАН?)

Как же выглядит автоматическое различение? Взгляните на следующий фрагмент кода из библиотеки автоматического дифференцирования на C ++ под названием STAN-MATH:

#include ‹stan / math.hpp›
int main () {
stan :: math :: var mu = 2, sigma = 3;
stan :: math :: var f = (mu-1) * (sigma + 1);
std :: cout ‹---------------- “f (mu, sigma) =« ‹< f.val () ‹< std :: endl;
f .grad (); // вычисляет градиент
std :: cout ‹* «df / d.mu =« ‹* mu.adj ()
‹< “df / d.sigma =« ‹< sigma.adj ( ) ‹---------------- std :: endl;
}

В этом фрагменте кода мы определяем функцию f как f = (mu-1) * (sigma + 1), как показано выше. Затем мы вычисляем его значение (f.val ()), когда mu = 2, а sigma = 3. Затем мы вычисляем градиенты f с помощью f.grad (), и, наконец, мы вычисляем частные производные каждой переменной с помощью вызова функции .adj ()! Это так просто!

Вернуться к теме

Хорошо, а как все это соотносится с тем, о чем идет речь в этом руководстве? На данный момент модули нейронной сети Shogun имеют функцию стоимости и частные производные, жестко запрограммированные. Мы хотим создать модуль, похожий на Tensorflow, который может оптимизировать любую заданную функцию затрат, используя stan-math в качестве инструмента для вычисления частных производных.

В этом руководстве я покажу, как вы можете продолжить создание модуля для оптимизации нейронных сетей в библиотеке, такой как Tensorflow, на C ++, так что следите за обновлениями!

Слово о сёгуне

Если вы хотите присоединиться к Open Source, Shogun - отличное место для начала. У них есть суперподдерживающее сообщество, и они приветствуют новичков в Opensource, так что загляните и посмотрите, сможете ли вы помочь с некоторыми проблемами, отмеченными на их github как «дружественные новичкам»!