NeuroPro

нейронные сети
и анализ данных

Начало
Новости
Услуги
Нейронные сети
Программы
Статьи
Заметки
Ссылки
Вопросы и ответы
Об авторе / контакты
Заметки

Гиперболизирующий тангенс (о "тормозных" нелинейностях нейронов)

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

Небольшое введение. В нейросетевых учебниках говорится, что более быструю сходимость процесса обучения нейронной сети алгоритмом обратного распространения ошибки обеспечивают симметричные относительно нуля (биполярные) нелинейные функции нейронов - униполярные функции дают сходимость медленнее. Наиболее часто используемая униполярная функция - сигмоида f(x)=1/(1+e(-x)), биполярная - гиперболический тангенс f(x)=tanh(x)=(ex2-1)/(ex2+1). Видим, что в обоих случаях используется вычисление ex.

Теперь программистские особенности. Экспонента входит в число базовых математических функций и реализована в стандартной математической библиотеке любого универсального (C/C++, объектный паскаль в его Delphi-инкарнации, Java,...) языка программирования. А поскольку трансляторы с этих языков позволяют (при определенных настройках компиляции) создавать программы, совместимые и с довольно старыми процессорами, то ВСЕ их библиотеки и математическая в том числе скомпилированы для совместимости с наиболее древними из поддерживаемых процессоров (а иначе придется со средой разработки поставлять несколько вариантов библиотек - по одной для каждого типа процессора). Если в программе исполнение функций из стандартных библиотек занимает небольшой процент общего времени, то мириться с возможной неоптимальностью библиотечного кода можно. Однако, давайте посмотрим внимательно на распределение вычислительных затрат при обучении нейросети. В этом может помочь программа-профилировщик (для среды программирования C++ Builder 6 я использовал профилировщик Intel VTune, в других системах может присутствовать "родной" профилировщик).

Почему C++ Builder 6? Потому, что предпочитаю сам программировать на ассемблере критичные по быстродействию фрагменты программ с задействованием SSE-команд современных процессоров (этими командами C++ Builder 6 пользоваться не умеет). И при использовании SSE-команд в описываемых ниже экспериментах получились бы еще более неприличные пропорции за счет почти четырехкратного уменьшения времени "прямого" функционирования адаптивных сумматоров нейронов, т.е. увеличения относительного времени вычисления нелинейных функций нейронов.

Вводная про эксперименты. Использовались сверточные нейронные сети (я их часто упоминаю в заметках в последнее время) размером 5-25-50-10 (5 плоскостей первого скрытого слоя размером 13*13 сверточных нейронов, 25 плоскостей второго скрытого слоя размером 5*5 сверточных нейронов, 50 обычных нейронов в третьем скрытом слое и 10 в выходном), решалась задача MNIST обучения распознаванию изображений рукописных цифр. Для эталонной сети замерялось время, затрачиваемое на первые 5 и последующие 15 эпох обучения: поскольку программа переставала обучать сеть на уверенно распознаваемых примерах, то на первых эпохах был более высок вклад от обратных функционирований нейронной сети и внесений коррекций в веса синапсов (при использованном в качестве нелинейной функции нейронов гиперболическом тангенсе это означает, что производные нелинейных функций не требуют вычислений ни экспоненты, ни гиперболического тангенса - т.е. на начальных эпохах вклад от гип.тангенса будет меньше, чем на последующих, где из-за снижения числа обратных распространений будет больше доля времени на прямые функционирования сети и вычисление нелинейной функции в т.ч.).

Изучались четыре варианта реализации гиперболического тангенса - функции tanh:

  1. Стандартная библиотечная функция tanh (которая вызывает стандартную же функцию exp).
  2. Функция tanh из модуля fastmath (функция exp при этом тоже будет использована из fastmath-реализации). Модуль fastmath имеется в версиях 6 Delphi и C++ Builder (а, может, и в версиях 5, и в последующих) и содержит ряд более быстрых реализаций стандартных математических функций. Напоминаю, речь идет о продуктах фирм Borland/Inprise/CodeGear и для других сред разработки данный вариант может быть не реализуем.
  3. Вручную запрограммированная на ассемблере функция tanh, включающая в себя код вычисления экспоненты (чтобы не тратить время на вызов отдельной внешней функции), избавленная от лишних проверок, наличествующих в стандартных и fastmath-вариантах функций tanh/exp, и более оптимизированная.
  4. В трех предыдущих вариантах экспонента вычислялась точно. Алгоритм приближенного вычисления экспоненты (предложен в статье 1999г, по ссылкам эта статья находится - я мог бы точно её указать, но оставлю работу читателям) включен в нашу функцию вычисления гиперболического тангенса.
Результаты для четырех описанных реализаций функции tanh и обучения эталонной нейронной сети на эпохах 1-5 и 6-20:

Вариант реализации функций tanh/expЭпохиВремя обучения, с. Ускорение времени обучения по сравнению с вариантом 1 % времени обучения, затраченный суммарно на tanh и exp % на tanh без exp% на отдельный exp Время, затраченное суммарно на tanh и exp, с. Ускорение времени, затраченного суммарно на tanh и exp, по сравнению с вариантом 1
11-5242.8-37.712.325.491.5-
6-20563.450.315.634.7283.4
21-5217.51.1230.913.417.567.21.4
6-20473.21.1942.318.423.9200.2
31-5197.01.2323.8-46.92.0
6-20412.01.3734.4141.7
41-5171.41.4212.220.94.4
6-20336.21.6819.063.9

Интерпретация полученных результатов для четырех вариантов.

  1. Кошмар: tanh - жутко медленная функция по сравнению с остальным кодом эмуляции нейронной сети (хорошо оптимизированным, но с большим потенциалом ускорения при использовании SSE-команд), на вычисление tanh и необходимое при этом вычисление экспоненты уходит до половины общего времени работы программы.
  2. Fastmath-реализации гиперболического тангенса и экспоненты действительно получаются суммарно быстрее. Но! Программисты, честно вспоминайте, в каком проценте книжек по C++ Builder или Delphi вы встречали упоминание о модуле fastmath? И еще честнее отвечайте - пользуетесь ли им в своих программах?
  3. Ручной ассемблерный код получается еще быстрее.
  4. Аппроксимация вычисления ex еще сильнее ускоряет вычисление.

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

Финальное ускорение времени обучения в среднем в полтора раза - совсем неплохо. При этом программа осталась совместима со старыми процессорами. Вопрос тем, кто смотрел исходные тексты нейросетевых библиотек: нелинейные функции нейронов там реализованы в виде какого-то оптимизированного варианта или путем вызовов "стандартных" функций? Вопрос не праздный - если посмотреть на последний столбец, где коэффициент ускорения считается не для общего времени работы программы, а для времени вычисления гиперболического тангенса, то при изменении реализации нелинейной функции она ускорилась в 4.4 раза. При этом начинали с не испорченного ничьими кривыми руками начального варианта. Т.е. вот он - пример возможности локального ускорения в разы, а из нескольких локальных ускорений достигается и большое общее ускорение.

Резюме. Экспонента или гиперболический тангенс в их "стандартных" для сред программирования реализациях не адаптированы для использования в программах моделирования нейронных сетей. Кроме совместимости со старыми процессорами (и потерей эффективности как для старых, так и для новых процессоров - ни рыба, ни мясо, в общем), реализации этих функций утяжелены проверками. Поэтому название заметки и говорит о гиперболизации времени обучения/функционирования нейронной сети при использовании стандартных библиотечных математических функций. Указанные возможности экономии были достижимы на любых Intel-совместимых процессорах. Т.е. вот уже более 20 лет, если отсчитывать время с момента разработки алгоритма обратного распространения, или 10 лет, если ориентироваться на самую быструю реализацию с аппроксимацией экспоненты из статьи 1999г, люди излишне долго ждали (и тратились на электричество для компьютеров) при обучении нейросетей, и, может, отказывались от экспериментов с нейронными сетями значительного размера. Однако, если программист квалифицированный (до уровня знания ассемблера и особенностей современных процессоров), то нейронные сети при их программной симуляции должны "летать".

И финальный вопрос. А, может, есть биполярная нелинейная сигмоидная функция, отличная от гиперболического тангенса? Ответ: да, есть, и она быстрее гиперболического тангенса с аппроксимированной экспонентой. Какие сделаете выводы?