c – 我的神经网络导致堆栈溢出

我很难生成一个最小的例子,因为我认为它与我的一些代码有关.但是,我相信我已经在下面提供了相关的代码.

我删除了一些我认为对问题不重要的课程等.

我有一个使用神经元的神经网络类:

神经元

template<std::size_t NumInputs>
class Neuron
{
public:
    Neuron()
    {
        for(auto& i : m_inputValues)
            i = 0;
        for(auto& e : m_eligibilityTraces)
            e = 0;
        for(auto& w : m_weights)
            w = 0;
        m_biasWeight = 0;
        m_biasEligibilityTrace = 0;
        m_outputValue = 0;
    }

    void SetInputValue(const std::size_t index, const double value)
    {
        m_inputValues[index] = value;
    }

    void SetWeight(const std::size_t index, const double weight)
    {
        if(std::isnan(weight))
            throw std::runtime_error("Shit! this is a nan bread");
        m_weights[index] = weight;
    }

    void SetBiasWeight(const double weight)
    {
        m_biasWeight = weight;
    }

    double GetInputValue(const std::size_t index) const
    {
        return m_inputValues[index];
    }

    double GetWeight(const std::size_t index) const
    {
        return m_weights[index];
    }

    double GetBiasWeight() const
    {
        return m_biasWeight;
    }

    double CalculateOutput()
    {
        double m_outputValue = 0;
        for(std::size_t i = 0; i < NumInputs; ++i)
        {
            m_outputValue += m_inputValues[i] * m_weights[i];
        }
        m_outputValue += 1.0 * m_biasWeight;
        m_outputValue = sigmoid(m_outputValue);
        return m_outputValue;
    }

    double GetOutput() const
    {
        return m_outputValue;
    }

    double GetEligibilityTrace(const std::size_t index) const
    {
        return m_eligibilityTraces[index];
    }

    void SetEligibilityTrace(const std::size_t index, const double eligibility)
    {
        m_eligibilityTraces[index] = eligibility;
    }

    void SetBiasEligibility(const double eligibility)
    {
        m_biasEligibilityTrace = eligibility;
    }

    double GetBiasEligibility() const
    {
        return m_biasEligibilityTrace;
    }

private:
    std::array<double,NumInputs> m_inputValues;
    std::array<double,NumInputs> m_weights;
    std::array<double,NumInputs> m_eligibilityTraces;
    double m_biasWeight;
    double m_biasEligibilityTrace;
    double m_outputValue;
};

神经网络

template<std::size_t NumInputs, std::size_t NumHidden, std::size_t NumOutputs>
class NeuralNetwork
{
public:

...

    std::array<double,NumOutputs> FeedForward(const std::array<double,NumInputs>& inputValues)
    {
        for(auto& hiddenNeuron : m_hiddenNeurons)
        {
            for(std::size_t i = 0; i < NumInputs; ++i)
                hiddenNeuron.SetInputValue(i,inputValues[i]);

            hiddenNeuron.CalculateOutput();
        }

        std::array<double, NumOutputs> returnValue;

        for(std::size_t h = 0; h < NumHidden; ++h)
        {
            auto hiddenOutput = m_hiddenNeurons[h].GetOutput();
            for(std::size_t o = 0; o < NumOutputs; ++o)
                m_outputNeurons[o].SetInputValue(h, hiddenOutput);
        }

        for(std::size_t o = 0; o < NumOutputs; ++o)
        {
            returnValue[o] = m_outputNeurons[o].CalculateOutput();
        }

        return returnValue;
    }

private:

    std::array<Neuron<NumInputs>,NumHidden> m_hiddenNeurons;
    std::array<Neuron<NumHidden>,NumOutputs> m_outputNeurons;
};

一切都适用于NeuralNetwork< 86,86,2>但在考虑到我需要更多输入变量之后,即NeuralNetwork< 170,170,2>当我启用-O2编译器标志时,FeedForward方法会产生堆栈溢出.使用-g标志设置不会产生此问题.

如果我删除FeedForward方法的这一部分,我不会得到堆栈溢出:

for(std::size_t h = 0; h < NumHidden; ++h)
{
    auto hiddenOutput = m_hiddenNeurons[h].GetOutput();
    for(std::size_t o = 0; o < NumOutputs; ++o)
        m_outputNeurons[o].SetInputValue(h, hiddenOutput);
}

我无法理解为什么会产生堆栈溢出.隐藏单位数为170,输出单位数为2;当然这还不足以导致溢出,特别是考虑到上面我通过170输入循环到170个隐藏单位.

正如您在Neuron类中看到的,GetOutput()方法不涉及任何其他函数调用,SetInputValue()也不会执行任何类似的操作.没有递归.

删除的部分没有内循环工作正常.但是下面的循环会导致堆栈溢出.

即这会导致堆栈溢出:

for(std::size_t h = 0; h < NumHidden; ++h)
{
    auto hiddenOutput = m_hiddenNeurons[h].GetOutput();
   // for(std::size_t o = 0; o < NumOutputs; ++o)
     //   m_outputNeurons[o].SetInputValue(h, hiddenOutput);
}

for(std::size_t o = 0; o < NumOutputs; ++o)
{
    returnValue[o] = m_outputNeurons[o].CalculateOutput();
}

然而,这不是:

for(std::size_t h = 0; h < NumHidden; ++h)
{
    auto hiddenOutput = m_hiddenNeurons[h].GetOutput();
   // for(std::size_t o = 0; o < NumOutputs; ++o)
     //   m_outputNeurons[o].SetInputValue(h, hiddenOutput);
}

for(std::size_t o = 0; o < NumOutputs; ++o)
{
    //returnValue[o] = m_outputNeurons[o].CalculateOutput();
}

这没有任何意义,因为循环不是嵌套的……

最佳答案 堆栈溢出仅在堆栈边界外的第一次实际写入时检测到,分别在实际命中保护页面时.因为你用Neuron类初始化0,所以这使得你的Neuron最初都是Nullbytes.这与您的环境初始化内存完全匹配(实际上未初始化,但映射到仅包含Nullbytes的共享只读页面).

一旦将第一个非空字节写入保护页面,它就会触发页面错误(如果写入该地址是合法的,则共享空页将被RAM中的实际页面替换).结果,然后检测到堆栈溢出,因为不应该写入该地址.

在你的情况下,你实际上已经离开了堆栈,并且分配后的所有内容都已经与堆发生冲突.你只是没注意到,因为后卫没有触发并完全被跳过.

将空页面映射到有效堆栈区域下方,而不是保留读取保护的保护页面或将其完全取消映射,这是特定于环境的.

堆栈和堆堆叠在一起,实际上你可以完全跳过防护页面,并且配置足够大,也是特定于环境的.根据您使用的编译器,您可以通过一个选项来捕获此错误,该选项强制堆栈分配以增量方式发生,一次最多一页. (例如 – 海湾合作委员会的检查 – )

使用像Valgrind这样的工具,它可以设置一个更加防御的环境来更容易地捕获这样的错误.然后,这将在创建数组时触发,而不是仅在第一次非零写入时触发.

点赞