Распараллеливание цикла for в Python

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

Я не уверен насчет реализации и того, как работает GIL, и актуально ли это для меня.

Код выглядит следующим образом:

class net():
    def train(inputs)
        ...
        for tset in batch:
            self.backprop(tset, learn_rate)

    def backprop(inputs):
        ...
        for c, w in zip(n.inconnections, range(len(n.inconnections))):
            c.current_error += n.current_error * n.inconnectionweights[w]
            n.icweightupdates[w] += - learn_rate * n.current_error * c.activation
        n.biasupdate += -learn_rate * n.current_error

Цикл в train() — это тот, который я хочу распараллелить, поскольку batch содержит набор обучающих выборок (20), которые можно обрабатывать независимо.


person Eumel    schedule 08.11.2017    source источник


Ответы (2)


В то время как банда евангелистов "AGILE" бьет в барабаны все громче и громче,

Никогда не начинайте программировать, пока не поймете, что это имеет смысл:

Почему?

Вы действительно можете потратить бесконечное количество времени на кодирование того, с чего даже начинать не имеет смысла, тогда как разумное количество времени, потраченное на системную инженерию, поможет вам определить, какие шаги разумно, а что нет.

Итак, давайте начнем с общей позиции: какова ваша мотивация для параллелизации -- Да, ПРОИЗВОДИТЕЛЬНОСТЬ.

Если мы оба согласны с этим, давайте рассмотрим эту новую область знаний.

Нетрудно набросать код, который в этом ужасно плох.

Становится все более важным уметь понимать и разрабатывать код, который может использовать современные аппаратные инфраструктуры с максимальной производительностью.

параллельная обработка в целом < a href="https://stackoverflow.com/tags/parallel-processing/info">предназначенный по многим другим причинам, а не только для производительности - ваш случай, как было предложено выше, находится в конце, на самом деле " просто"-[CONCURRENT]-тип планирования процесса. Неправда-[PARALLEL]? Конечно, если бы это было не на машине с более чем 21 процессорным ядром, выключенным HyperThreading и всеми процессами операционной системы, остановленными на момент существования, пока не будут обработаны все 20 примеров туда и обратно, во всех глобальных циклах минимайзера сойдутся .

И представьте, вы пытаетесь запустить всего 20 входных данных машинного обучения (примеры), в то время как в реальных наборах данных есть много тысяч (намного больше многих сотен тысяч в моей проблемной области) примеров для обработки, поэтому вы никогда не дойдете до такой крайности в настоящее-[PARALLEL] планирование процесса, используя этот способ.

Лучше начать понимать Закон Амдала в его полном контексте сначала ( наивная формулировка оказывает плохую услугу для экспериментов первокурсников - лучше освоить full- Подробности также приведены в разделе «Критика» обновленного поста, сначала о накладных расходах и ограничении ресурсов, а затем проголосовать за «распараллеливание любой ценой» -- < em>даже если с полдюжины "подражателей-гуру" посоветуют вам сделать то-то и то-то и так-то многие пиар-медиа сегодня кричат, что вы идете-параллельно ).

Далее прочитайте это о деталях и различиях, которые могут возникнуть только из-за использования лучших или более подходящих инструментов (после понимания Закон Амдаля Принципиальные потолки ускорения, ускорение +512x шокирует вас и заставит нутром чувствовать, что имеет смысл, а что нет). Это актуально для каждого обзора узких мест производительности и реинжиниринга. Большинство нейронных сетей тратят огромное количество времени (не из-за низкой производительности кода, а из-за огромных размеров DataSET) на повторный запуск Feed-Forward + Back- Этапы распространения, на которых векторизованный код разработать сложнее, чем использовать простой код Python, но именно здесь производительность может быть увеличена, а не потеряна.

Python может скорее использовать интеллектуальные инструменты векторизации из numpy, а также может спроектировать ваш код так, чтобы использовать минимальные накладные расходы за счет систематического использования представления, вместо того, чтобы постоянно терять производительность при выделении памяти после передачи копий плотных файлов. матрицы в нейронных сетях, если python-реализации должны показать его возможные скорости.

Есть так много случаев, когда простое кодирование приведет к ужасно плохой производительности обработки при любом мастерстве кода, в то время как немного математики покажет умную реинжиниринг вычислительного процесса, и производительность процесса внезапно подскочила. на несколько порядков (да, в 10, 100 раз быстрее, просто используя человеческий мозг и критическое мышление).

Да, спроектировать быстрый код сложно, но никто же не обещал вам, что ужин будет бесплатным, не так ли?


Последний, но тем не менее важный:

никогда не позволяйте всем [CONCURRENT]-задачам выполнять одну и ту же работу, тем более постоянно повторять ее:

for c, w in zip( n.inconnections, range( len( n.inconnections ) ) ):
    ...

этот синтаксис легко закодировать, но он вводит повторно вычисляемую zip() обработку в каждую задачу, требующую ускорения. Нет. Действительно плохая идея, если производительность все еще в виду.

person user3666197    schedule 08.11.2017
comment
основная цель состояла в том, чтобы иметь очень понятный код, больше как доказательство концепции. Желание распараллелить это было просто из-за того, что я посмотрел на диспетчер задач и подумал, что он использует только 20% ЦП, возможно, он будет работать немного быстрее, если будет работать параллельно. Тем не менее, отличный ответ и хорошая ссылка, я надеюсь, что он получит больше голосов, чтобы было легче искать других людей. - person Eumel; 09.11.2017
comment
Рад, что вы нашли ответ полезным и вдохновляющим. StackOveflow предлагает пользователям щелкнуть стрелку [UpVote] и [Accept], чтобы показать, что какой-то вопрос или ответ были интересны. Не стесняйтесь делать то же самое. - person user3666197; 09.11.2017

Потоки Python не сделают это быстрее из-за GIL. Вместо этого вы можете попробовать процессы:

import multiprocessing

class net():
    def train(inputs)
        ...
        with multiprocessing.Pool() as p:
            biasupdates = p.map(lambda tset: self.backprop(tset, learn_rate), batch)
        n.biasupdate += sum(biasupdates)

    def backprop(inputs):
        ...
        for c, w in zip(n.inconnections, range(len(n.inconnections))):
            c.current_error += n.current_error * n.inconnectionweights[w]
            n.icweightupdates[w] += - learn_rate * n.current_error * c.activation
        return -learn_rate * n.current_error
person Valentin Lorentz    schedule 08.11.2017
comment
почему именно вы поместили предвзятые обновления во внешний цикл? Я также напрямую сохраняю обновления (смещение и вес) в переменных моих нейронов, для чего мне нужен возврат? - person Eumel; 08.11.2017
comment
При всем уважении, Валентин, вы не рассказали Юмелу всю историю — дополнительные расходы, которые вы рекомендуете заплатить за создание multiprocessing.Pool(), являются самым большим грехом, который профессионал в области компьютерных наук может совершить по отношению к заинтересованный студент. В то время как ваш код может оставаться синтаксически правильным (и сдержал обещание избежать GIL-шагового танца «утка-утка-вперед»), он принципиально ошибочен 1) из-за добавления огромной установки/завершения надстроек. накладные расходы на обработку + 2) за счет повторения огромной части работы внутри параллельных блоков кода. Возможно, вы захотите пересмотреть пост. - person user3666197; 08.11.2017
comment
@Eumel Я сделал это, потому что backprop запускается в отдельном процессе, поэтому он не может напрямую изменять переменные в исходном процессе. Возврат заключается в отправке значения исходному процессу. - person Valentin Lorentz; 08.11.2017
comment
@user3666197 user3666197 Я знаю, но стоит заплатить накладные расходы, если backprop выполняется долго. Я просто предложил, OP может протестировать обе версии и оставить самую быструю. - person Valentin Lorentz; 08.11.2017
comment
Валентин, NN-backprop никогда запускает много времени. Если бы это было так, вся артиллерия нейронной сети стала бы бесполезной, поскольку ее почти невозможно обучить в ближайшем будущем. Верно как раз обратное. Backprop — это самая совершенная часть программного обеспечения NN. Выдвижение предложения делегировать право на однократное создание экземпляра процесса для выполнения одного шага обратного распространения — худшее, что можно сделать для повышения производительности — вы оплатите все накладные расходы, связанные с процессом + все затраты на выборку памяти, но никогда, НИКОГДА не используйте повторно строку кеша, так как сразу же завершаете процесс. - person user3666197; 09.11.2017