Параллелизация нейросетей – 2
Пятница, Июнь 4th, 2010Параллелизация нейросетей – 2
В прошлой заметке описывалась довольно сложный способ параллелизации вычислений. Нейроны (нейрокластеры) делились на блоки и распределялись между потоками, некоторые данные дублировались, данные о связях также распределялись по потокам.
Но вот вычислительных ядер становится все больше и больше. Может оказаться, что лучше написать более вычислительно сложный код, но написать его раньше. Идея из функционального программирования – никаких особых синхронизаций не нужно, главное, чтобы функция не давала никаких побочных эффектов. Идею вспомнил во время чтения http://graphics.cs.williams.edu/archive/SweeneyHPG2009/TimHPG2009.pdf, стр 44 (ссылка с хабра). Каждая итерация каждого потока модифицирует данные только одного нейрона.
Код превращается во что-то типа:
* Создать несколько потоков
В каждом потоке:
* выбрать очередной необработанный нейрон
* обработать
В целях ускорения, очередность выборки нейронов потоками можно ставить в зависимости от потока.
Дальнейшая оптимизация: после завершения обработки «своих» нейронов, поток может перейти на обработку «чужих». Чтобы синхронизация проводилась не на каждом нейроне, распределять нейроны можно небольшими группами. Величину групп можно варьировать в зависимости от количества связей в нейронах. Пересчет границ групп можно проводить редко.
Недостаток подхода – лишний доступ к памяти при проверке «не нужно ли добавить активацию с этого нейрона», если нейрон отправки не активен. В некоторых ИНС это может увеличивать количество необходимой работы в 10-100 раз. Конечно, если потоков 100-1000, то и такой поворот событий сулит выигрыш :) Если в ИНС много пересылок «подпороговых» сигналов (подпороговых к высокочастотной активации), то такой способ более эффективен. Другой способ:
В каждом потоке выделяется интервал идентификаторов нейронов, который он имеет право менять. Для каждого потока выполняется функция:
Если данный нейрон должен модифицировать другие нейроны, то начать проход по связям
Если связь ведет к нейрону, идентификатор которого содержится в интервале, выделенном данному потоку– то его можно модифицировать.
Выигрыш такого подхода: в сравнении с предыдущим способом, обработка только активных связей (в некоторых случаях в 10-100 меньше работы), но лишняя проверка интервала на каждой связи, и количество проходов по памяти прямо пропорционально количеству потоков. В случае отсортированного списка связей, проверку вхождения нейрона в интервал можно делать только одну – вначале на одну границу интервала (например, «меньше»), а затем, после начала блока подходящих идентификаторов – только другую границу. После выхода за границу можно сразу переходить к следующему нейрону. Это может в 2 раза сократить обработку лишних связей. Еще в 2 раза – если начинать движение с начала или с конца списка связей в зависимости от интервала нейронов.
Вывод – вначале выгоднее попробовать схему с тупой синхронизацией нейронов назначения :)


