Как получить больше производительности от автоматической дифференциации?

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

В основном мой код является переводом кода старых документов, который написан в Matlab и C. Я его не оценил, но это код работает с несколькими итерациями в секунду. Мина находится в порядке минут на итерацию...

Код доступен в этих репозиториях:

Этот код можно запустить, выполнив следующие команды:

$ cd aer-utils
$ cabal sandbox init
$ cabal sandbox add-source ../aer
$ cabal run learngabors

Используя средства профилирования GHCs, я подтвердил, что спуск фактически является частью, которая занимает большую часть времени:

Flamegraph of one iteration

(интерактивная версия здесь: https://dl.dropboxusercontent.com/u/2359191/learngabors.svg)

-s говорит мне, что производительность довольно низкая:

Productivity  33.6% of total user, 33.6% of total elapsed

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

  • Unboxing: В настоящее время я использую реализацию пользовательской матрицы (в src/Data/SimpleMat.hs). Это был единственный способ получить ad для работы с матрицами (см. Как сделать автоматическое дифференцирование на hmatrix?). Я предполагаю, что использование матричного типа, такого как newtype Mat w h a = Mat (Unboxed.Vector a), обеспечит лучшую производительность из-за распаковки и слияния. Я нашел некоторый код, который имеет экземпляры ad для распакованных векторов, но до сих пор я не смог использовать их с помощью conjugateGradientFunction.

  • Матричные производные: В электронном письме, которое я просто не могу найти в тот момент, Эдвард упоминает, что было бы лучше использовать экземпляры Forward для матричных типов вместо матриц, заполненных Forward экземпляры. Я имею слабое представление о том, как этого достичь, но еще не понял, как реализовать его в терминах классов типа ad.

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

Ответы

Ответ 1

В настоящее время вы используете сценарий для наименьшего сценария для текущей библиотеки ad.

FWIW. Вы не сможете использовать существующие классы/типы ad с "матричным/векторным объявлением". Это было бы довольно большое инженерное усилие, см. https://github.com/ekmett/ad/issues/2

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

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

Если у вас есть шанс качать # haskell-lens на irc.freenode.net, я с удовольствием расскажу о проектах в этом пространстве и дам совет. Алекс Ланг также много работал над ad и часто присутствует там и может иметь идеи.