如何在不对反向传播ANN进行并行处理的情况下提高性能

问题描述 投票:15回答:4

profiling后向传播算法之后,我了解到它负责占用我60%的计算时间。在开始研究并行替代方案之前,我想看看是否还有其他可以做的事情。

activate(const double input[])功能的配置仅占用〜5%的时间。gradient(const double input)功能的实现如下:

inline double gradient(const double input) { return (1 - (input * input)); }

有关的培训功能:

void train(const vector<double>& data, const vector<double>& desired, const double learn_rate, const double momentum) {
        this->activate(data);
        this->calculate_error(desired);

        // adjust weights for layers
        const auto n_layers = this->config.size();
        const auto adjustment = (1 - momentum) * learn_rate;

        for (size_t i = 1; i < n_layers; ++i) {
            const auto& inputs = i - 1 > 0 ? this->outputs[i - 1] : data;
            const auto n_inputs = this->config[i - 1];
            const auto n_neurons = this->config[i];

            for (auto j = 0; j < n_neurons; ++j) {
                const auto adjusted_error = adjustment * this->errors[i][j];

                for (auto k = 0; k < n_inputs; ++k) {
                    const auto delta = adjusted_error * inputs[k] + (momentum * this->deltas[i][j][k]);

                    this->deltas[i][j][k] = delta;
                    this->weights[i][j][k] += delta;
                }

                const auto delta = adjusted_error * this->bias + (momentum * this->deltas[i][j][n_inputs]);

                this->deltas[i][j][n_inputs] = delta;
                this->weights[i][j][n_inputs] += delta;
            }
        }
    }
}

此问题可能更适合https://codereview.stackexchange.com/

c++ optimization const
4个回答
7
投票

如果想训练/使用神经网络,就无法避免O(n ^ 2)算法。但这非常适合矢量算术。例如,通过巧妙地使用SSE或AVX,您可以将神经元分成4或8个块进行处理,并使用乘加法而不是两个单独的指令。

[如果您使用现代的编译器并仔细地重新构造算法并使用正确的开关,则甚至可以让编译器为您自动对循环进行矢量化,但是您的工作量可能会有所不同。

对于gcc,使用-O3或-ftree-vectorize激活自动矢量化。当然,您需要一个具有向量功能的CPU,具体取决于目标CPU,例如-march = core2 -mssse4.1或类似的东西。如果使用-ftree-vectorizer-verbose = 2,则会得到详细的说明,以及为什么不对循环进行矢量化的原因。看看http://gcc.gnu.org/projects/tree-ssa/vectorization.html

当然,更好的是直接使用编译器内部函数。


2
投票

您要在此处从循环内部消除条件:

const double lower_layer_output = i > 0 ? outputs[lower_layer][k] : input[k]; // input layer semantics

您可以通过提前计算第零次迭代(i == 0的特殊情况)来消除此条件。

        deltas[i][j][k] = delta;
        weights[i][j][k] += delta;

您提到使用std :: vector,所以这是vector的vector的vector吗?您的数据将是连续的(除非每个向量都连续)。考虑使用C样式数组。这些尺寸有多大?如果非常大,可能会有一些缓存注意事项。例如。您不希望最后一个下标[k]刷新L1缓存。有时打破循环以一次处理较小范围的k个索引会有所帮助(strip mining)。

您还可以对unrolling的内部循环进行一些试验,例如尝试在循环内执行4或8个操作。分别递增4/8,并在另一个循环中处理任何余数。编译器可能已经在这样做了。

正如其他人提到的那样,使用SIMD(SSE / AVX)可能是获得最大收益的地方。您可以使用编译器intrinsics(链接到Visual Studio,但gcc支持相同的语法)或以汇编形式(内联或其他方式)编写。如您所述,跨多个内核扩展是另一个方向。 OpenMP可以帮助您轻松完成任务。

有时从代码中生成带注释的assembly listing很有用,以尝试查看编译器在哪些方面做得不好。

This是有关优化的出色的一般资源。


1
投票
我不确定编译器是否可以在您的情况下对其进行优化,但是将inverse_momentum * (learn_rate * errors[i][j])移出外部变量以在较低的循环中循环“ k”可能会减轻CPU的负担。

1
投票
我不喜欢valarray,但我有一种直觉,认为周围的人有很多机会。
© www.soinside.com 2019 - 2024. All rights reserved.