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

标签 c++ stack-overflow

我很难给出一个最小的例子,因为我认为它与我的更多代码有关。但是,我相信我已经在下面提供了相关代码。

我删除了我认为对问题不是很重要的类等部分。

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

神经元

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> FeedForward当我启用 -O2 时,方法会产生堆栈溢出编译器标志。 -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();
}

这没有意义,因为循环没有嵌套...

最佳答案

堆栈溢出仅在第一个实际 写入堆栈边界外时检测到,分别是在实际到达保护页时。由于您正在用 0 初始化 Neuron 类中的所有内容,这使得您的 Neuron 最初都是 Nullbytes。这与您的环境将内存初始化为的内容完全匹配(实际上并未初始化,而是映射到仅包含 Nullbytes 的共享只读页面)。

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

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

在有效堆栈区域下方映射一个空页面,而不是留下一个读保护保护页面或让它完全未映射是特定于环境的。

让堆栈和堆靠近在一起,您实际上可以完全跳过保护页面,分配足够大,这也是特定于环境的。根据您使用的编译器,您可以使用一个选项来捕获此错误,该选项强制堆栈分配以增量方式发生,一次最多一页。 (例如 GCC 的 -fstack-check。)

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

关于c++ - 我的神经网络导致堆栈溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47567390/

相关文章:

c++ - 使用外部SDK工具链文件的VisualStudio上的CMake项目编译错误

c++ - 指针 vector : Why does changing the pointer externally not change the vector element?

c - 声明大指针数组时 C 中的段错误

c# - 如何用几行代码产生 StackOverflowException?

java - Url Shortener 重定向到 index.html

android - ViewGroup 在 dispatchDraw 时导致 StackOverflow

javascript - QML 旋转排列项目

c++ - C++ 中的 101 Qt 样式表边距填充 hack 指南。设置填充不起作用

c++ - 如何打印 const void* 的值

c++ - 将大型堆栈分配数组作为函数参数传递会导致堆栈溢出