2

我编写了一个运行速度非常慢的神经网络,所以我希望通过并行化某个循环来加快它的速度。

我不确定 GIL 的实施以及 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)。

4

2 回答 2

1

当“ AGILE ”传福音团的鼓声越来越响,

在您知道这样做有意义之前,切勿开始编码:

为什么?

您确实可以花费无限的时间来编写一个甚至没有任何意义的东西,而花在适当的系统工程上的合理时间将帮助您确定哪些步骤是合理的,哪些不是。

所以,让我们从一个共同点开始:并行化的动机是什么——是的,性能。

如果我们都同意这一点,让我们回顾一下这个新的专业领域。

编写代码并不难,这在这方面非常糟糕。

能够理解和设计代码变得越来越重要,这可以利用当代硬件基础设施来发挥最大性能。

通常是出于许多其他原因,而不仅仅是性能 - 如上所述,您的情况实际上是“公正” -[CONCURRENT]类型的 process-scheduling。不是真的- [PARALLEL]?当然,如果它不是在具有超过 21 个 CPU 内核的机器上,超线程关闭并且所有操作系统进程暂时停止,直到所有 20 个示例都在那里和回来处理,在所有全局最小化循环中收敛.

想象一下,您尝试只运行 20 个机器学习输入(示例),而现实世界的 DataSET 有数千个(远远超出我的问题域中的数十万个)示例要处理,因此永远不会陷入如此极端的境地一个真正的[PARALLEL]进程调度,使用这种方式。

最好首先从完整的上下文中理解阿姆达尔定律(一个开销天真的公式在这方面对新生的实验很不利——更好地掌握 更新后的关于开销和资源的帖子的批评部分的详细信息——在投票支持“不惜一切代价进行并行化”之前,首先要限制限制——即使有六位“想要的大师”建议你这样做,而且今天有如此多的公关动机媒体呼吁你去并行化) .

接下来,阅读这篇关于细节和差异的文章,这可能只是来自使用更好或更合适的工具(一旦了解了阿姆达尔定律的主要加速上限,+512 倍的加速将震惊你并设定直觉,什么造就什么?没有意义)。这与每次性能瓶颈审查和重新设计有关。与使用_普通的 python 代码,但这是可以获得而不是损失性能的地方。

Python 可以使用numpyplus 的智能矢量化工具,可以通过系统地使用视图来设计您的代码以利用最小开销,而不是在神经网络中传递密集矩阵的副本后反复失去进行内存分配的性能,如果 python-实现是为了展示它可能的速度。

在很多情况下,无论代码可能采用何种工艺,仅编码都会产生非常糟糕的处理性能,而一些数学将显示计算过程的智能重新设计,并且处理性能突然跃升几个数量级(是的,仅通过使用人脑和批判性思维就可以快 10 倍、100 倍)

是的,很难设计一个快速的代码,但是没有人答应你免费吃晚餐,是吗?


最后但并非最不重要的:

永远不要让所有[CONCURRENT]任务都做完全相同的工作,越少总是重复它:

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

zip()这种语法很容易编码,但在每个“想要加速的”任务中引入了重新计算的处理。不。确实是个坏主意,如果仍然考虑性能的话。

于 2017-11-08T17:07:30.610 回答
0

由于 GIL,Python 线程不会使这更快。相反,您可以尝试以下流程:

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
于 2017-11-08T15:04:05.120 回答