пятница, 10 августа 2018 г.

Два стиля программирования в MXNet

MXNet поддерживает два стиля программирования: императивное программирование (посредством NDArray API) и символическое программирование (посредством Symbol API). В кратце императивное программирование - это стиль программирования наиболее всем знакомый. Здесь если A и B - это переменные обозначающие матрицы, тогда C = A + B это отрывок кода, при исполнении которого выполняется суммирование значений связанных с A и B и сохранение этой суммы в новую переменную C. Символическое программирование, с другой стороны, позволяет функциям быть определенным абстрактно через вычислительный граф. В символическом стиле сначала выражаются сложные функции в качестве заполнителя значений. Затем можно исполнять эти функции, связывая их с реальными значениями.

Императивное программирование с NDArray

Если вы знакомы в NumPy, тогда механика NDArray должна быть вам привычна. Как и соответственно numpy.ndarray, mxnet.ndarray (mxnet.nd для краткости) позволяет выразить и манипулировать многоразмерными гомогенными массивами компонентов фиксированного размера. Преобразование между ними двумя очень простое:

# Создание numpy массива в mxnet NDArray
A_np = np.array([[0,1,2,3,4],[5,6,7,8,9]])
A_nd = nd.array(A_np)
# Преобразование обратно в numpy массив
A2_np = A_nd.asnumpy()

Другие библиотеки для глубокого обучения имеют тенденцию полагаться исключительно на NumPy в императивном программировании и в синтаксисе. Зачем тогда необходимо использовать NDArray? Проще говоря, другие библиотеки используют преимущества GPU вычислений только когда исполняют символические функции. Используя NDArray, MXNet пользователи могут определять устройство контекста и использовать GPU. Другими словами MXNet дает доступ к высокопроизводительным вычислениям для императивных операций, в то время как TensorFlow и Theano только для символических операций.

X = mx.nd.array([[1,2],[3,4]])
Y = mx.nd.array([[5,6],[7,8]])
result = X + Y

Символическое программирование в MXNet

Вдобавок к предоставлению быстрых математических операций через NDArray, MXNet предоставляет интерфейс для определения операций абстрактно через вычислительный граф. С mxnet.symbol операции определяются абстрактно как заполнители значений. Например, в следующем коде a и b обозначают реальные значения, которые поступят в ходе работы. Когда вызывается c = a + b, никакие вычисления не производятся. Эта операция просто строит граф, который определяет взаимоотношения между a, b и с. для выполнения реальных вычислений необходимо привязать c к реальным значениям.

a = mx.sym.Variable('a')
b = mx.sym.Variable('b')
c = a + b
executor = c.bind(mx.cpu(), {'a': X, 'b': Y})
result = executor.forward()

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

Построение моделей с MXNet слоями

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

Предопределенные слои позволяют выражать большие модели кратко. Повторяющаяся работа, такая как выделение параметров и вывод их измерений, исключена. Слои написаны напрямую на C++, предоставляя лучшую производительность, чем эквивалентные слои реализованные руками на Python.

data = mx.symbol.Variable('data')

fc1 = mx.symbol.FullyConnected(data = data, name='fc1', num_hidden=128)

act1 = mx.symbol.Activation(data = fc1, name='relu1', act_type="relu")

fc2 = mx.symbol.FullyConnected(data = act1, name = 'fc2', num_hidden = 64)

act2 = mx.symbol.Activation(data = fc2, name='relu2', act_type="relu")

fc3 = mx.symbol.FullyConnected(data = act2, name='fc3', num_hidden=num_classes)

mlp = mx.symbol.SoftmaxOutput(data = fc3, name = 'softmax')