我很难生成一个最小的例子,因为我认为它与我的一些代码有关.但是,我相信我已经在下面提供了相关的代码.
我删除了一些我认为对问题不重要的课程等.
我有一个使用神经元的神经网络类:
神经元
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这样的工具,它可以设置一个更加防御的环境来更容易地捕获这样的错误.然后,这将在创建数组时触发,而不是仅在第一次非零写入时触发.